mirror of https://github.com/grpc/grpc.git
Merge pull request #13207 from kkm000/package-grpc-tools
Integrate Grpc.Tools into msbuild system (C# only)pull/16910/head
commit
e69a636f32
37 changed files with 3008 additions and 35 deletions
@ -0,0 +1,85 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
namespace Grpc.Tools.Tests |
||||
{ |
||||
public class CSharpGeneratorTest : GeneratorTest |
||||
{ |
||||
GeneratorServices _generator; |
||||
|
||||
[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); |
||||
} |
||||
|
||||
[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("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")); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,88 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.IO; |
||||
using NUnit.Framework; |
||||
|
||||
namespace Grpc.Tools.Tests |
||||
{ |
||||
public class CppGeneratorTest : GeneratorTest |
||||
{ |
||||
GeneratorServices _generator; |
||||
|
||||
[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); |
||||
} |
||||
|
||||
[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("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")); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,146 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.IO; |
||||
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 \
|
||||
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\
|
||||
C:/usr/include/google/protobuf/any.proto\
|
||||
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 \
|
||||
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 { } |
||||
} |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,55 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using Microsoft.Build.Framework; |
||||
using Microsoft.Build.Utilities; |
||||
using Moq; |
||||
using NUnit.Framework; |
||||
|
||||
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"); |
||||
} |
||||
|
||||
[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); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,78 @@ |
||||
<Project Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
||||
<Import Project="..\Grpc.Core\Version.csproj.include" /> |
||||
|
||||
<PropertyGroup> |
||||
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks> |
||||
<OutputType>Exe</OutputType> |
||||
</PropertyGroup> |
||||
|
||||
<Import Project="..\Grpc.Core\SourceLink.csproj.include" /> |
||||
|
||||
<!-- This is copied verbatim from Grpc.Core/Common.csproj.include. Other settings |
||||
in that file conflict with the intent of this build, as it cannot be signed, |
||||
and may not compile Grpc.Core/Version.cs, as that file references constants |
||||
in Grpc.Core.dll. |
||||
TODO(kkm): Refactor imports. --> |
||||
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT' and '$(MSBuildRuntimeType)' == 'Core' "> |
||||
<!-- Use Mono reference assemblies in SDK build: https://github.com/dotnet/sdk/issues/335 --> |
||||
<FrameworkPathOverride Condition="Exists('/usr/lib/mono/4.5-api')">/usr/lib/mono/4.5-api</FrameworkPathOverride> |
||||
<FrameworkPathOverride Condition="Exists('/usr/local/lib/mono/4.5-api')">/usr/local/lib/mono/4.5-api</FrameworkPathOverride> |
||||
<FrameworkPathOverride Condition="Exists('/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5-api')">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5-api</FrameworkPathOverride> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Grpc.Tools\Grpc.Tools.csproj" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup> |
||||
<PackageReference Include="Moq" Version="4.8.3" /> |
||||
<PackageReference Include="NUnit; NUnitLite" Version="3.10.1" /> |
||||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" /> |
||||
</ItemGroup> |
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' != 'net45' "> |
||||
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' "> |
||||
<Reference Include="Microsoft.Build.Framework; Microsoft.Build.Utilities.v4.0" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net45' "> |
||||
<PackageReference Include="Microsoft.Build.Framework; Microsoft.Build.Utilities.Core" Version="15.6.*" /> |
||||
</ItemGroup> |
||||
|
||||
<!-- Groups below is a hack to allow the test to run under Mono Framework build. |
||||
========================================================================== --> |
||||
|
||||
<!-- Mono unfortunately comes with broken Microsoft.Build.* assemblies installed in |
||||
the GAC, but fortunately searches for runtime assemblies in a different order |
||||
than Windows CLR host: the GAC assemblies have the lowest search priority, (see |
||||
https://www.mono-project.com/docs/advanced/assemblies-and-the-gac/), not the |
||||
highest as is in Windows (documented at |
||||
https://docs.microsoft.com/dotnet/framework/deployment/how-the-runtime-locates-assemblies). |
||||
To run the tests under Mono, we need correct assemblies in the same directory as |
||||
the test executable. Correct versions are in the MSBuild directory under Mono. --> |
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' and '$(OS)' != 'Windows_NT' "> |
||||
<None Include="$(_MSBuildAssemblyPath)/Microsoft.Build.Framework.dll; |
||||
$(_MSBuildAssemblyPath)/Microsoft.Build.Utilities.v4.0.dll; |
||||
$(_MSBuildAssemblyPath)/Microsoft.Build.Utilities.Core.dll" |
||||
CopyToOutputDirectory="Always" Visible="false" /> |
||||
</ItemGroup> |
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net45' and '$(OS)' != 'Windows_NT' "> |
||||
<!-- The None items are included into assembly candidate resolution by default, and |
||||
we do not want that, as they are not valid as reference assemblies (the version of |
||||
Microsoft.Build.Utilities.v4.0 is a pure facade for Microsoft.Build.Utilities.Core, |
||||
and does not define any types at all). Exclude them from assembly resolution. See |
||||
https://github.com/Microsoft/msbuild/blob/50639058f/documentation/wiki/ResolveAssemblyReference.md --> |
||||
<AssemblySearchPaths>{HintPathFromItem};{TargetFrameworkDirectory};{RawFileName}</AssemblySearchPaths> |
||||
<!-- Mono knows better where its MSBuild is. --> |
||||
<_MSBuildAssemblyPath Condition=" '$(MSBuildRuntimeType)' != 'Core' " |
||||
>$(MSBuildToolsPath)</_MSBuildAssemblyPath> |
||||
<!-- Under dotnet, make the best guess we can. --> |
||||
<_MSBuildAssemblyPath Condition=" '$(MSBuildRuntimeType)' == 'Core' " |
||||
>$(FrameworkPathOverride)/../msbuild/$(MSBuildToolsVersion)/bin</_MSBuildAssemblyPath> |
||||
</PropertyGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,33 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.Reflection; |
||||
using NUnitLite; |
||||
|
||||
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); |
||||
#else |
||||
new AutoRun().Execute(args); |
||||
#endif |
||||
}; |
||||
} |
@ -0,0 +1,76 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.Reflection; // UWYU: Object.GetType() extension. |
||||
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; } |
||||
|
||||
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")); |
||||
|
||||
LastPathToTool = pathToTool; |
||||
LastResponseFile = response.Remove(response.Length - 1).Split('\n'); |
||||
|
||||
// Do not run the tool, but pretend it ran successfully. |
||||
return 0; |
||||
} |
||||
}; |
||||
|
||||
protected Mock<IBuildEngine> _mockEngine; |
||||
protected ProtoCompileTestable _task; |
||||
|
||||
[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>()); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,179 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.IO; |
||||
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[] { |
||||
"--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)); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,51 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
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"); |
||||
|
||||
_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); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,139 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.Runtime.InteropServices; // For NETCORE tests. |
||||
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"); |
||||
} |
||||
|
||||
#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); |
||||
} |
||||
} |
||||
|
||||
#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); |
||||
} |
||||
|
||||
#endif // NETCORE |
||||
}; |
||||
} |
@ -0,0 +1,46 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
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; |
||||
} |
||||
|
||||
// Return an array of items from given itemspecs. |
||||
public static ITaskItem[] MakeSimpleItems(params string[] specs) |
||||
{ |
||||
return specs.Select(s => new TaskItem(s)).ToArray(); |
||||
} |
||||
}; |
||||
} |
@ -1,33 +0,0 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<package> |
||||
<metadata> |
||||
<id>Grpc.Tools</id> |
||||
<title>gRPC C# Tools</title> |
||||
<summary>Tools for C# implementation of gRPC - an RPC library and framework</summary> |
||||
<description>Precompiled protobuf compiler and gRPC protobuf compiler plugin for generating gRPC client/server C# code. Binaries are available for Windows, Linux and MacOS.</description> |
||||
<version>$version$</version> |
||||
<authors>Google Inc.</authors> |
||||
<owners>grpc-packages</owners> |
||||
<licenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</licenseUrl> |
||||
<projectUrl>https://github.com/grpc/grpc</projectUrl> |
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance> |
||||
<releaseNotes>Release $version$</releaseNotes> |
||||
<copyright>Copyright 2015, Google Inc.</copyright> |
||||
<tags>gRPC RPC Protocol HTTP/2</tags> |
||||
</metadata> |
||||
<files> |
||||
<!-- forward slashes in src path enable building on Linux --> |
||||
<file src="protoc_plugins/protoc_windows_x86/protoc.exe" target="tools/windows_x86/protoc.exe" /> |
||||
<file src="protoc_plugins/protoc_windows_x86/grpc_csharp_plugin.exe" target="tools/windows_x86/grpc_csharp_plugin.exe" /> |
||||
<file src="protoc_plugins/protoc_windows_x64/protoc.exe" target="tools/windows_x64/protoc.exe" /> |
||||
<file src="protoc_plugins/protoc_windows_x64/grpc_csharp_plugin.exe" target="tools/windows_x64/grpc_csharp_plugin.exe" /> |
||||
<file src="protoc_plugins/protoc_linux_x86/protoc" target="tools/linux_x86/protoc" /> |
||||
<file src="protoc_plugins/protoc_linux_x86/grpc_csharp_plugin" target="tools/linux_x86/grpc_csharp_plugin" /> |
||||
<file src="protoc_plugins/protoc_linux_x64/protoc" target="tools/linux_x64/protoc" /> |
||||
<file src="protoc_plugins/protoc_linux_x64/grpc_csharp_plugin" target="tools/linux_x64/grpc_csharp_plugin" /> |
||||
<file src="protoc_plugins/protoc_macos_x86/protoc" target="tools/macosx_x86/protoc" /> |
||||
<file src="protoc_plugins/protoc_macos_x86/grpc_csharp_plugin" target="tools/macosx_x86/grpc_csharp_plugin" /> |
||||
<file src="protoc_plugins/protoc_macos_x64/protoc" target="tools/macosx_x64/protoc" /> |
||||
<file src="protoc_plugins/protoc_macos_x64/grpc_csharp_plugin" target="tools/macosx_x64/grpc_csharp_plugin" /> |
||||
</files> |
||||
</package> |
@ -0,0 +1,114 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using System.IO; |
||||
using System.Runtime.CompilerServices; |
||||
using System.Runtime.InteropServices; |
||||
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"; |
||||
}; |
||||
|
||||
// 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; |
||||
|
||||
// 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() |
||||
{ |
||||
#if NETCORE |
||||
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; |
||||
} |
||||
#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) |
||||
{ |
||||
// 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; |
||||
#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; |
||||
}; |
||||
|
||||
// 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); |
||||
} |
||||
} |
@ -0,0 +1,273 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
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]; |
||||
} |
||||
|
||||
// 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(); |
||||
} |
||||
|
||||
/// <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(); |
||||
} |
||||
|
||||
/// <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")); |
||||
} |
||||
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}"); |
||||
} |
||||
return new string[0]; |
||||
} |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,194 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using System.IO; |
||||
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); |
||||
} |
||||
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(); |
||||
} |
||||
}; |
||||
|
||||
// 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; |
||||
} |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,101 @@ |
||||
<Project Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
||||
<Import Project="..\Grpc.Core\Version.csproj.include" /> |
||||
|
||||
<PropertyGroup> |
||||
<AssemblyName>Protobuf.MSBuild</AssemblyName> |
||||
<Version>$(GrpcCsharpVersion)</Version> |
||||
<!-- If changing targets, change also paths in Google.Protobuf.Tools.targets. --> |
||||
<TargetFrameworks>net45;netstandard1.3</TargetFrameworks> |
||||
</PropertyGroup> |
||||
|
||||
<!-- This is copied verbatim from Grpc.Core/Common.csproj.include. Other settings |
||||
in that file conflict with the intent of this build, as it cannot be signed, |
||||
and may not compile Grpc.Core/Version.cs, as that file references constants |
||||
in Grpc.Core.dll. |
||||
TODO(kkm): Refactor imports. --> |
||||
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT' and '$(MSBuildRuntimeType)' == 'Core' "> |
||||
<!-- Use Mono reference assemblies in SDK build: https://github.com/dotnet/sdk/issues/335 --> |
||||
<FrameworkPathOverride Condition="Exists('/usr/lib/mono/4.5-api')">/usr/lib/mono/4.5-api</FrameworkPathOverride> |
||||
<FrameworkPathOverride Condition="Exists('/usr/local/lib/mono/4.5-api')">/usr/local/lib/mono/4.5-api</FrameworkPathOverride> |
||||
<FrameworkPathOverride Condition="Exists('/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5-api')">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5-api</FrameworkPathOverride> |
||||
</PropertyGroup> |
||||
|
||||
<PropertyGroup Label="Asset root folders. TODO(kkm): Change with package separation."> |
||||
<!-- TODO(kkm): Rework whole section when splitting packages. --> |
||||
<!-- GRPC: ../../third_party/protobuf/src/google/protobuf/ --> |
||||
<!-- GPB: ../src/google/protobuf/ --> |
||||
<Assets_ProtoInclude>../../../third_party/protobuf/src/google/protobuf/</Assets_ProtoInclude> |
||||
|
||||
<!-- GPB: ../protoc/ --> |
||||
<!-- GRPC: ../protoc_plugins/protoc_ --> |
||||
<Assets_ProtoCompiler>../protoc_plugins/protoc_</Assets_ProtoCompiler> |
||||
|
||||
<!-- GRPC: ../protoc_plugins/ --> |
||||
<Assets_GrpcPlugins>../protoc_plugins/</Assets_GrpcPlugins> |
||||
</PropertyGroup> |
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' != 'net45' "> |
||||
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants> |
||||
</PropertyGroup> |
||||
|
||||
<PropertyGroup Label="NuGet package definition" Condition=" '$(Configuration)' == 'Release' "> |
||||
<!-- TODO(kkm): Change to "build\" after splitting. --> |
||||
<BuildOutputTargetFolder>build\_protobuf\</BuildOutputTargetFolder> |
||||
<DevelopmentDependency>true</DevelopmentDependency> |
||||
<NoPackageAnalysis>true</NoPackageAnalysis> |
||||
<PackageId>Grpc.Tools</PackageId> |
||||
<Description>gRPC and Protocol Buffer compiler for managed C# and native C++ projects. |
||||
|
||||
Add this package to a project that contains .proto files to be compiled to code. |
||||
It contains the compilers, include files and project system integration for gRPC |
||||
and Protocol buffer service description files necessary to build them on Windows, |
||||
Linux and MacOS. Managed runtime is supplied separately in the Grpc.Core package.</Description> |
||||
<Copyright>Copyright 2018 gRPC authors</Copyright> |
||||
<Authors>gRPC authors</Authors> |
||||
<PackageLicenseUrl>https://github.com/grpc/grpc/blob/master/LICENSE</PackageLicenseUrl> |
||||
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl> |
||||
<PackageTags>gRPC RPC protocol HTTP/2</PackageTags> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup Label="NuGet package assets"> |
||||
<None Pack="true" PackagePath="build\" Include="build\**\*.xml; build\**\*.props; build\**\*.targets;" /> |
||||
|
||||
<!-- Protobuf assets (for Google.Protobuf.Tools) --> |
||||
<_ProtoAssetName Include="any;api;descriptor;duration;empty;field_mask; |
||||
source_context;struct;timestamp;type;wrappers" /> |
||||
<_Asset PackagePath="build/native/include/google/protobuf/" Include="@(_ProtoAssetName->'$(Assets_ProtoInclude)%(Identity).proto')" /> |
||||
|
||||
<!-- TODO(kkm): GPB builds assets into "macosx", GRPC into "macos". --> |
||||
<!-- TODO(kkm): Do not place non-tools under tools/, use build/native/bin/. --> |
||||
<!-- TODO(kkm): Do not package windows x64 builds (#13098). --> |
||||
<_Asset PackagePath="tools/windows_x86/protoc.exe" Include="$(Assets_ProtoCompiler)windows_x86/protoc.exe" /> |
||||
<_Asset PackagePath="tools/windows_x64/protoc.exe" Include="$(Assets_ProtoCompiler)windows_x64/protoc.exe" /> |
||||
<_Asset PackagePath="tools/linux_x86/protoc" Include="$(Assets_ProtoCompiler)linux_x86/protoc" /> |
||||
<_Asset PackagePath="tools/linux_x64/protoc" Include="$(Assets_ProtoCompiler)linux_x64/protoc" /> |
||||
<_Asset PackagePath="tools/macosx_x86/protoc" Include="$(Assets_ProtoCompiler)macos_x86/protoc" /> <!-- GPB: macosx--> |
||||
<_Asset PackagePath="tools/macosx_x64/protoc" Include="$(Assets_ProtoCompiler)macos_x64/protoc" /> <!-- GPB: macosx--> |
||||
|
||||
<!-- gRPC assets (for Grpc.Tools) --> |
||||
<_Asset PackagePath="tools/windows_x86/grpc_csharp_plugin.exe" Include="$(Assets_GrpcPlugins)protoc_windows_x86/grpc_csharp_plugin.exe" /> |
||||
<_Asset PackagePath="tools/windows_x64/grpc_csharp_plugin.exe" Include="$(Assets_GrpcPlugins)protoc_windows_x64/grpc_csharp_plugin.exe" /> |
||||
<_Asset PackagePath="tools/linux_x86/grpc_csharp_plugin" Include="$(Assets_GrpcPlugins)protoc_linux_x86/grpc_csharp_plugin" /> |
||||
<_Asset PackagePath="tools/linux_x64/grpc_csharp_plugin" Include="$(Assets_GrpcPlugins)protoc_linux_x64/grpc_csharp_plugin" /> |
||||
<_Asset PackagePath="tools/macosx_x86/grpc_csharp_plugin" Include="$(Assets_GrpcPlugins)protoc_macos_x86/grpc_csharp_plugin" /> |
||||
<_Asset PackagePath="tools/macosx_x64/grpc_csharp_plugin" Include="$(Assets_GrpcPlugins)protoc_macos_x64/grpc_csharp_plugin" /> |
||||
|
||||
<None Include="@(_Asset)" Pack="true" Visible="false" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' "> |
||||
<Reference Include="Microsoft.Build.Framework; Microsoft.Build.Utilities.v4.0" Pack="false" /> |
||||
</ItemGroup> |
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net45' "> |
||||
<PackageReference Include="Microsoft.Build.Framework; Microsoft.Build.Utilities.Core" Version="15.6.*" /> |
||||
<!-- Set PrivateAssets="All" on all items, even those implicitly added, |
||||
so that they do not become dependencies of this package. --> |
||||
<PackageReference Update="@(PackageReference)" PrivateAssets="All" /> |
||||
</ItemGroup> |
||||
|
||||
</Project> |
@ -0,0 +1,441 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
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; } |
||||
|
||||
/// <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(); |
||||
} |
||||
|
||||
// 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'); |
||||
} |
||||
}; |
||||
|
||||
// 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; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,86 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
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; } |
||||
|
||||
/// <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; } |
||||
|
||||
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(); |
||||
|
||||
return !Log.HasLoggedErrors; |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,78 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
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; } |
||||
|
||||
/// <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; } |
||||
|
||||
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; |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,63 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2018 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
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 |
||||
{ |
||||
/// <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; |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,11 @@ |
||||
<?xml version="1.0"?> |
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
</PropertyGroup> |
||||
|
||||
<!-- Name of this file must match package ID. --> |
||||
<!-- Packages will be split later. --> |
||||
<Import Project="_grpc/_Grpc.Tools.props"/> |
||||
<Import Project="_protobuf/Google.Protobuf.Tools.props"/> |
||||
</Project> |
@ -0,0 +1,11 @@ |
||||
<?xml version="1.0"?> |
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
</PropertyGroup> |
||||
|
||||
<!-- Name of this file must match package ID. --> |
||||
<!-- Packages will be split later. --> |
||||
<Import Project="_grpc/_Grpc.Tools.targets"/> |
||||
<Import Project="_protobuf/Google.Protobuf.Tools.targets"/> |
||||
</Project> |
@ -0,0 +1,30 @@ |
||||
<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties"> |
||||
<Rule Name="ProtoBuf" |
||||
DisplayName="File Properties" |
||||
PageTemplate="generic" |
||||
Description="File Properties" |
||||
OverrideMode="Extend"> |
||||
<Rule.DataSource> |
||||
<DataSource Persistence="ProjectFile" Label="Configuration" ItemType="ProtoBuf" |
||||
HasConfigurationCondition="false" SourceOfDefaultValue="AfterContext" /> |
||||
</Rule.DataSource> |
||||
|
||||
<Rule.Categories> |
||||
<Category Name="gRPC" DisplayName="gRPC" /> |
||||
</Rule.Categories> |
||||
|
||||
<EnumProperty Name="GrpcServices" DisplayName="gRPC Stub Classes" |
||||
Category="gRPC" Default="Both" |
||||
Description="Generate gRPC server and client stub classes."> |
||||
<EnumValue Name="Both" DisplayName="Client and Server" IsDefault="true" /> |
||||
<EnumValue Name="Client" DisplayName="Client only" /> |
||||
<EnumValue Name="Server" DisplayName="Server only" /> |
||||
<EnumValue Name="None" DisplayName="Do not generate" /> |
||||
<EnumProperty.DataSource> |
||||
<DataSource ItemType="ProtoBuf" SourceOfDefaultValue="AfterContext" |
||||
PersistenceStyle="Attribute" /> |
||||
</EnumProperty.DataSource> |
||||
</EnumProperty> |
||||
|
||||
</Rule> |
||||
</ProjectSchemaDefinitions> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0"?> |
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
</PropertyGroup> |
||||
</Project> |
@ -0,0 +1,48 @@ |
||||
<?xml version="1.0"?> |
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
<gRPC_PluginFileName Condition=" '$(gRPC_PluginFileName)' == '' and '$(Language)' == 'C#' ">grpc_csharp_plugin</gRPC_PluginFileName> |
||||
</PropertyGroup> |
||||
|
||||
<ItemGroup Condition=" '$(Protobuf_ProjectSupported)' == 'true' and '$(Language)' == 'C#' "> |
||||
<!-- Extend property pages with gRPC properties. --> |
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Grpc.CSharp.xml"> |
||||
<Context>File;BrowseObject</Context> |
||||
</PropertyPageSchema> |
||||
</ItemGroup> |
||||
|
||||
<ItemDefinitionGroup Condition=" '$(Protobuf_ProjectSupported)' == 'true' and '$(Language)' == 'C#' "> |
||||
<ProtoBuf> |
||||
<GrpcServices Condition=" '%(ProtoBuf.GrpcServices)' == '' ">Both</GrpcServices> |
||||
</ProtoBuf> |
||||
</ItemDefinitionGroup> |
||||
|
||||
<!-- This target is invoked in a C# project, or can be called in a customized project. --> |
||||
<Target Name="gRPC_ResolvePluginFullPath" AfterTargets="Protobuf_ResolvePlatform"> |
||||
<PropertyGroup> |
||||
<!-- TODO(kkm): Do not use Protobuf_PackagedToolsPath, roll gRPC's own. --> |
||||
<!-- TODO(kkm): Do not package windows x64 builds (#13098). --> |
||||
<gRPC_PluginFullPath Condition=" '$(gRPC_PluginFullPath)' == '' and '$(Protobuf_ToolsOs)' == 'windows' " |
||||
>$(Protobuf_PackagedToolsPath)\$(Protobuf_ToolsOs)_x86\$(gRPC_PluginFileName).exe</gRPC_PluginFullPath> |
||||
<gRPC_PluginFullPath Condition=" '$(gRPC_PluginFullPath)' == '' " |
||||
>$(Protobuf_PackagedToolsPath)/$(Protobuf_ToolsOs)_$(Protobuf_ToolsCpu)/$(gRPC_PluginFileName)</gRPC_PluginFullPath> |
||||
</PropertyGroup> |
||||
</Target> |
||||
|
||||
<Target Name="_gRPC_PrepareCompileOptions" AfterTargets="Protobuf_PrepareCompileOptions"> |
||||
<ItemGroup Condition=" '$(Language)' == 'C#' "> |
||||
<Protobuf_Compile Condition=" %(Protobuf_Compile.GrpcServices) != 'None' "> |
||||
<GrpcPluginExe Condition=" '%(Protobuf_Compile.GrpcPluginExe)' == '' ">$(gRPC_PluginFullPath)</GrpcPluginExe> |
||||
<GrpcOutputDir Condition=" '%(Protobuf_Compile.GrpcOutputDir)' == '' " >%(Protobuf_Compile.OutputDir)</GrpcOutputDir> |
||||
<_GrpcOutputOptions Condition=" '%(Protobuf_Compile.Access)' == 'Internal' ">%(Protobuf_Compile._GrpcOutputOptions);internal_access</_GrpcOutputOptions> |
||||
</Protobuf_Compile> |
||||
<Protobuf_Compile Condition=" '%(Protobuf_Compile.GrpcServices)' == 'Client' "> |
||||
<_GrpcOutputOptions>%(Protobuf_Compile._GrpcOutputOptions);no_server</_GrpcOutputOptions> |
||||
</Protobuf_Compile> |
||||
<Protobuf_Compile Condition=" '%(Protobuf_Compile.GrpcServices)' == 'Server' "> |
||||
<_GrpcOutputOptions>%(Protobuf_Compile._GrpcOutputOptions);no_client</_GrpcOutputOptions> |
||||
</Protobuf_Compile> |
||||
</ItemGroup> |
||||
</Target> |
||||
</Project> |
@ -0,0 +1,24 @@ |
||||
<?xml version="1.0"?> |
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
|
||||
<!-- Revision number of this package conventions (as if "API" version). --> |
||||
<Protobuf_ToolingRevision>1</Protobuf_ToolingRevision> |
||||
|
||||
<!-- TODO(kkm): Remove one "../" when separating packages. --> |
||||
<!-- TODO(kkm): Do not place non-tools under tools/, use build/native/bin/. --> |
||||
<Protobuf_PackagedToolsPath>$( [System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)../../tools) )</Protobuf_PackagedToolsPath> |
||||
<Protobuf_StandardImportsPath>$( [System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)../native/include) )</Protobuf_StandardImportsPath> |
||||
</PropertyGroup> |
||||
|
||||
<!-- NET SDK projects only: include proto files by default. Other project |
||||
types are not setting or using $(EnableDefaultItems). |
||||
Note that MSBuild evaluates all ItemGroups and their conditions in the |
||||
final pass over the build script, so properties like EnableDefaultProtoBufItems |
||||
here can be changed later in the project. --> |
||||
<ItemGroup Condition=" '$(Protobuf_ProjectSupported)' == 'true' "> |
||||
<ProtoBuf Include="**/*.proto" |
||||
Condition=" '$(EnableDefaultItems)' == 'true' and '$(EnableDefaultProtoBufItems)' == 'true' " /> |
||||
</ItemGroup> |
||||
</Project> |
@ -0,0 +1,384 @@ |
||||
<?xml version="1.0"?> |
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
<!-- We allow a non-C# generator be set by the user, but skip adding outputs to Compile in this case. --> |
||||
<Protobuf_Generator Condition=" '$(Protobuf_Generator)' == '' and '$(Language)' == 'C#' ">CSharp</Protobuf_Generator> |
||||
<!-- Configuration is passing the smoke test. --> |
||||
<Protobuf_ProjectSupported Condition=" '$(Protobuf_Generator)' != '' ">true</Protobuf_ProjectSupported> |
||||
<_Protobuf_MsBuildAssembly Condition=" '$(MSBuildRuntimeType)' == 'Core' ">netstandard1.3\Protobuf.MSBuild.dll</_Protobuf_MsBuildAssembly> |
||||
<_Protobuf_MsBuildAssembly Condition=" '$(MSBuildRuntimeType)' != 'Core' ">net45\Protobuf.MSBuild.dll</_Protobuf_MsBuildAssembly> |
||||
</PropertyGroup> |
||||
|
||||
<UsingTask AssemblyFile="$(_Protobuf_MsBuildAssembly)" TaskName="Grpc.Tools.ProtoToolsPlatform" /> |
||||
<UsingTask AssemblyFile="$(_Protobuf_MsBuildAssembly)" TaskName="Grpc.Tools.ProtoCompilerOutputs" /> |
||||
<UsingTask AssemblyFile="$(_Protobuf_MsBuildAssembly)" TaskName="Grpc.Tools.ProtoReadDependencies" /> |
||||
<UsingTask AssemblyFile="$(_Protobuf_MsBuildAssembly)" TaskName="Grpc.Tools.ProtoCompile" /> |
||||
|
||||
<PropertyGroup Condition=" '$(Protobuf_ProjectSupported)' == 'true' "> |
||||
<Protobuf_IntermediatePath Condition=" '$(Protobuf_IntermediatePath)' == '' ">$(IntermediateOutputPath)</Protobuf_IntermediatePath> |
||||
<Protobuf_OutputPath Condition=" '$(Protobuf_OutputPath)' == '' ">$(Protobuf_IntermediatePath)</Protobuf_OutputPath> |
||||
<Protobuf_DepFilesPath Condition=" '$(Protobuf_DepFilesPath)' == '' ">$(Protobuf_IntermediatePath)</Protobuf_DepFilesPath> |
||||
</PropertyGroup> |
||||
|
||||
<ItemDefinitionGroup Condition=" '$(Protobuf_ProjectSupported)' == 'true' and '$(Language)' == 'C#' "> |
||||
<ProtoBuf> |
||||
<Access Condition="'%(ProtoBuf.Access)' == '' ">Public</Access> |
||||
<ProtoCompile Condition="'%(ProtoBuf.ProtoCompile)' == '' ">True</ProtoCompile> |
||||
<ProtoRoot Condition="'%(ProtoBuf.ProtoRoot)' == '' " /> |
||||
<CompileOutputs Condition="'%(ProtoBuf.CompileOutputs)' == ''">True</CompileOutputs> |
||||
<OutputDir Condition="'%(ProtoBuf.OutputDir)' == '' ">$(Protobuf_OutputPath)</OutputDir> |
||||
</ProtoBuf> |
||||
</ItemDefinitionGroup> |
||||
|
||||
<ItemGroup Condition=" '$(Protobuf_ProjectSupported)' == 'true' and '$(Language)' == 'C#' "> |
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)Protobuf.CSharp.xml"> |
||||
<Context>File;BrowseObject</Context> |
||||
</PropertyPageSchema> |
||||
<AvailableItemName Include="ProtoBuf" /> |
||||
</ItemGroup> |
||||
|
||||
<PropertyGroup> |
||||
<!-- NET SDK: by default, do not include proto files in the directory. |
||||
Current Microsoft's recommendation is against globbing: |
||||
https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#recommendation --> |
||||
<EnableDefaultProtoBufItems Condition=" '$(EnableDefaultProtoBufItems)' == '' ">false</EnableDefaultProtoBufItems> |
||||
</PropertyGroup> |
||||
|
||||
<!-- Check configuration sanity before build. --> |
||||
<Target Name="_Protobuf_SanityCheck" BeforeTargets="PrepareForBuild"> |
||||
<Error |
||||
Condition=" '$(Protobuf_ProjectSupported)' != 'true' " |
||||
Text="Google.Protobuf.Tools proto compilation is only supported by default in a C# project (extension .csproj)" /> |
||||
</Target> |
||||
|
||||
<!--================================================================================ |
||||
Tool path resolution |
||||
=================================================================================--> |
||||
|
||||
<!-- Extension point for plugin packages: use Protobuf_ToolsOs and Protobuf_ToolsCpu |
||||
to resolve executable. Either or both may be blank, however, if resolution |
||||
fails; do check them before using. --> |
||||
<Target Name="Protobuf_ResolvePlatform"> |
||||
<ProtoToolsPlatform> |
||||
<Output TaskParameter="Os" PropertyName="_Protobuf_ToolsOs"/> |
||||
<Output TaskParameter="Cpu" PropertyName="_Protobuf_ToolsCpu"/> |
||||
</ProtoToolsPlatform> |
||||
|
||||
<PropertyGroup> |
||||
<!-- First try environment variable. --> |
||||
<Protobuf_ToolsOs>$(PROTOBUF_TOOLS_OS)</Protobuf_ToolsOs> |
||||
<Protobuf_ToolsCpu>$(PROTOBUF_TOOLS_CPU)</Protobuf_ToolsCpu> |
||||
<Protobuf_ProtocFullPath>$(PROTOBUF_PROTOC)</Protobuf_ProtocFullPath> |
||||
|
||||
<!-- Next try OS and CPU resolved by ProtoToolsPlatform. --> |
||||
<Protobuf_ToolsOs Condition=" '$(Protobuf_ToolsOs)' == '' ">$(_Protobuf_ToolsOs)</Protobuf_ToolsOs> |
||||
<Protobuf_ToolsCpu Condition=" '$(Protobuf_ToolsCpu)' == '' ">$(_Protobuf_ToolsCpu)</Protobuf_ToolsCpu> |
||||
<!-- TODO(kkm): Do not package windows x64 builds (#13098). --> |
||||
<Protobuf_ProtocFullPath Condition=" '$(Protobuf_ProtocFullPath)' == '' and '$(Protobuf_ToolsOs)' == 'windows' " |
||||
>$(Protobuf_PackagedToolsPath)\$(Protobuf_ToolsOs)_x86\protoc.exe</Protobuf_ProtocFullPath> |
||||
<Protobuf_ProtocFullPath Condition=" '$(Protobuf_ProtocFullPath)' == '' " |
||||
>$(Protobuf_PackagedToolsPath)/$(Protobuf_ToolsOs)_$(Protobuf_ToolsCpu)/protoc</Protobuf_ProtocFullPath> |
||||
</PropertyGroup> |
||||
|
||||
<Error Condition=" '$(DesignTimeBuild)' != 'true' and '$(PROTOBUF_PROTOC)' == '' |
||||
and ( '$(Protobuf_ToolsOs)' == '' or '$(Protobuf_ToolsCpu)' == '' ) " |
||||
Text="Google.Protobuf.Tools cannot determine host OS and CPU. Use environment variables PROTOBUF_TOOLS_OS={linux|macosx|windows} and PROTOBUF_TOOLS_CPU={x86|x64} to try the closest match to your system. You may also set PROTOBUF_PROTOC to specify full path to the host-provided compiler (v3.5+ is required)." /> |
||||
</Target> |
||||
|
||||
<!--================================================================================ |
||||
Proto compilation |
||||
=================================================================================--> |
||||
|
||||
<!-- Extension points. --> |
||||
<Target Name="Protobuf_BeforeCompile" /> |
||||
<Target Name="Protobuf_AfterCompile" /> |
||||
|
||||
<!-- Main compile sequence. Certain steps are gated by the value $(DesignTimeBuild), |
||||
so the sequence is good for either design time or build time. --> |
||||
<Target Name="Protobuf_Compile" |
||||
Condition=" '@(ProtoBuf)' != '' " |
||||
DependsOnTargets=" Protobuf_BeforeCompile; |
||||
Protobuf_ResolvePlatform; |
||||
_Protobuf_SelectFiles; |
||||
Protobuf_PrepareCompile; |
||||
_Protobuf_AugmentLanguageCompile; |
||||
_Protobuf_CoreCompile; |
||||
Protobuf_ReconcileOutputs; |
||||
Protobuf_AfterCompile" /> |
||||
|
||||
<!-- Do proto compilation by default in a C# project. In other types, the user invoke |
||||
Protobuf_Compile directly where required. --> |
||||
<!-- TODO(kkm): Do shared compile in outer multitarget project? --> |
||||
<Target Name="_Protobuf_Compile_BeforeCsCompile" |
||||
BeforeTargets="BeforeCompile" |
||||
DependsOnTargets="Protobuf_Compile" |
||||
Condition=" '$(Language)' == 'C#' " /> |
||||
|
||||
<Target Name="_Protobuf_SelectFiles"> |
||||
<!-- Guess .proto root for the files. Whenever the root is set for a file explicitly, |
||||
leave it as is. Otherwise, for files under the project directory, set the root |
||||
to "." for the project's directory, as it is the current when compiling; for the |
||||
files outside of project directory, use each .proto file's directory as the root. --> |
||||
<FindUnderPath Path="$(MSBuildProjectDirectory)" |
||||
Files="@(ProtoBuf->WithMetadataValue('ProtoRoot',''))"> |
||||
<Output TaskParameter="InPath" ItemName="_Protobuf_NoRootInProject"/> |
||||
<Output TaskParameter="OutOfPath" ItemName="_Protobuf_NoRootElsewhere"/> |
||||
</FindUnderPath> |
||||
<ItemGroup> |
||||
<!-- Files with explicit metadata. --> |
||||
<Protobuf_Compile Include="@(ProtoBuf->HasMetadata('ProtoRoot'))" /> |
||||
<!-- In-project files will have ProtoRoot='.'. --> |
||||
<Protobuf_Compile Include="@(_Protobuf_NoRootInProject)"> |
||||
<ProtoRoot>.</ProtoRoot> |
||||
</Protobuf_Compile> |
||||
<!-- Out-of-project files will have respective ProtoRoot='%(RelativeDir)'. --> |
||||
<Protobuf_Compile Include="@(_Protobuf_NoRootElsewhere)"> |
||||
<ProtoRoot>%(RelativeDir)</ProtoRoot> |
||||
</Protobuf_Compile> |
||||
<!-- Remove files not for compile. --> |
||||
<Protobuf_Compile Remove="@(Protobuf_Compile)" Condition=" !%(ProtoCompile) " /> |
||||
<!-- Ensure invariant Source=%(Identity). --> |
||||
<Protobuf_Compile> |
||||
<Source>%(Identity)</Source> |
||||
</Protobuf_Compile> |
||||
</ItemGroup> |
||||
</Target> |
||||
|
||||
<!-- Extension point for non-C# project. Protobuf_Generator should be supported |
||||
by the ProtoCompile task, but we skip inferring expected outputs. All proto |
||||
files will be always recompiled with a warning, unless you add expectations |
||||
to the Protobuf_ExpectedOutputs collection. |
||||
|
||||
All inferred ExpectedOutputs will be added to code compile (C#) in a C# project |
||||
by default. This is controlled per-proto by the CompileOutputs metadata. --> |
||||
<Target Name="Protobuf_PrepareCompile" Condition=" '@(Protobuf_Compile)' != '' "> |
||||
<!-- Predict expected names. --> |
||||
<ProtoCompilerOutputs Condition=" '$(Language)' == 'C#' " |
||||
ProtoBuf="@(Protobuf_Compile)" |
||||
Generator="$(Protobuf_Generator)"> |
||||
<Output TaskParameter="PossibleOutputs" ItemName="Protobuf_ExpectedOutputs" /> |
||||
</ProtoCompilerOutputs> |
||||
<!-- Read any dependency files from previous compiles. --> |
||||
<ProtoReadDependencies Condition=" '$(Protobuf_DepFilesPath)' != '' and '$(DesignTimeBuild)' != 'true' " |
||||
ProtoBuf="@(Protobuf_Compile)" |
||||
ProtoDepDir="$(Protobuf_DepFilesPath)" > |
||||
<Output TaskParameter="Dependencies" ItemName="Protobuf_Dependencies" /> |
||||
</ProtoReadDependencies> |
||||
</Target> |
||||
|
||||
<!-- Add all expected outputs, and only these, to language compile. --> |
||||
<Target Name="_Protobuf_AugmentLanguageCompile" |
||||
DependsOnTargets="_Protobuf_EnforceInvariants" |
||||
Condition=" '$(Language)' == 'C#' "> |
||||
<ItemGroup> |
||||
<_Protobuf_CodeCompile Include="@(Protobuf_ExpectedOutputs->Distinct())" |
||||
Condition=" '%(Source)' != '' and '@(Protobuf_Compile->WithMetadataValue('CompileOutputs', 'true'))' != '' " /> |
||||
<Compile Include="@(_Protobuf_CodeCompile)" /> |
||||
</ItemGroup> |
||||
</Target> |
||||
|
||||
<!-- These invariants must be kept for compile up-to-date check to work. --> |
||||
<Target Name="_Protobuf_EnforceInvariants"> |
||||
<!-- Enforce Source=Identity on proto files. The 'Source' metadata is used as a common |
||||
key to match dependencies/expected outputs in the lists for up-to-date checks. --> |
||||
<ItemGroup> |
||||
<Protobuf_Compile> |
||||
<Source>%(Identity)</Source> |
||||
</Protobuf_Compile> |
||||
</ItemGroup> |
||||
|
||||
<!-- Remove possible output and dependency declarations that have no Source set, or those |
||||
not matching any proto marked for compilation. --> |
||||
<ItemGroup> |
||||
<Protobuf_ExpectedOutputs Remove="@(Protobuf_ExpectedOutputs)" Condition=" '%(Protobuf_ExpectedOutputs.Source)' == '' " /> |
||||
<Protobuf_ExpectedOutputs Remove="@(Protobuf_ExpectedOutputs)" Condition=" '%(Source)' != '' and '@(Protobuf_Compile)' == '' " /> |
||||
<Protobuf_Dependencies Remove="@(Protobuf_Dependencies)" Condition=" '%(Protobuf_Dependencies.Source)' == '' " /> |
||||
<Protobuf_Dependencies Remove="@(Protobuf_Dependencies)" Condition=" '%(Source)' != '' and '@(Protobuf_Compile)' == '' " /> |
||||
</ItemGroup> |
||||
</Target> |
||||
|
||||
<!-- Gather files with and without known outputs, separately. --> |
||||
<Target Name="_Protobuf_GatherStaleFiles" |
||||
Condition=" '@(Protobuf_Compile)' != '' " |
||||
DependsOnTargets="_Protobuf_EnforceInvariants; _Protobuf_GatherStaleSimple; _Protobuf_GatherStaleBatched"> |
||||
<ItemGroup> |
||||
<!-- Drop outputs from MSBuild inference (they won't have the '_Exec' metadata). --> |
||||
<_Protobuf_OutOfDateProto Remove="@(_Protobuf_OutOfDateProto->WithMetadataValue('_Exec',''))" /> |
||||
</ItemGroup> |
||||
</Target> |
||||
|
||||
<Target Name="_Protobuf_GatherStaleSimple"> |
||||
<!-- Simple selection: always compile files that have no declared outputs (but warn below). --> |
||||
<ItemGroup> |
||||
<_Protobuf_OutOfDateProto Include="@(Protobuf_Compile)" |
||||
Condition = " '%(Source)' != '' and '@(Protobuf_ExpectedOutputs)' == '' "> |
||||
<_Exec>true</_Exec> |
||||
</_Protobuf_OutOfDateProto> |
||||
</ItemGroup> |
||||
|
||||
<!-- You are seeing this warning because there was no Protobuf_ExpectedOutputs items with |
||||
their Source attribute pointing to the proto files listed in the warning. Such files |
||||
will be recompiled on every build, as there is nothing to run up-to-date check against. |
||||
Set Protobuf_NoOrphanWarning to 'true' to suppress if this is what you want. --> |
||||
<Warning Condition=" '@(_Protobuf_OutOfDateProto)' != '' and '$(Protobuf_NoOrphanWarning)' != 'true' " |
||||
Text="The following files have no known outputs, and will be always recompiled as if out-of-date: @(_Protobuf_Orphans->' %(Identity)', '')" /> |
||||
</Target> |
||||
|
||||
<Target Name="_Protobuf_GatherStaleBatched" |
||||
Inputs="@(Protobuf_Compile);%(Source);@(Protobuf_Dependencies);$(MSBuildAllProjects)" |
||||
Outputs="@(Protobuf_ExpectedOutputs)" > |
||||
<!-- The '_Exec' metadatum is set to distinguish really executed items from those MSBuild so |
||||
"helpfully" infers in a bucketed task. For the same reason, cannot use the intrinsic |
||||
ItemGroup task here. --> |
||||
<CreateItem Include="@(Protobuf_Compile)" AdditionalMetadata="_Exec=true"> |
||||
<Output TaskParameter="Include" ItemName="_Protobuf_OutOfDateProto"/> |
||||
</CreateItem> |
||||
</Target> |
||||
|
||||
<!-- Extension point: Plugins massage metadata into recognized metadata |
||||
values passed to the ProtoCompile task. --> |
||||
<Target Name="Protobuf_PrepareCompileOptions" Condition=" '@(Protobuf_Compile)' != '' "> |
||||
<ItemGroup> |
||||
<Protobuf_Compile> |
||||
<_OutputOptions Condition=" '%(Protobuf_Compile.Access)' == 'Internal' ">%(Protobuf_Compile._OutputOptions);internal_access</_OutputOptions> |
||||
</Protobuf_Compile> |
||||
</ItemGroup> |
||||
</Target> |
||||
|
||||
<Target Name="_Protobuf_CoreCompile" |
||||
Condition=" '$(DesignTimeBuild)' != 'true' " |
||||
DependsOnTargets="Protobuf_PrepareCompileOptions;_Protobuf_GatherStaleFiles"> |
||||
<!-- Ensure output directories. --> |
||||
<MakeDir Directories="%(_Protobuf_OutOfDateProto.OutputDir)" /> |
||||
<MakeDir Directories="%(_Protobuf_OutOfDateProto.GrpcOutputDir)" /> |
||||
<MakeDir Directories="$(Protobuf_DepFilesPath)" /> |
||||
|
||||
<!-- Force output to the current directory if the user has set it to empty. --> |
||||
<ItemGroup> |
||||
<_Protobuf_OutOfDateProto> |
||||
<OutputDir Condition=" '%(OutputDir)' == '' ">.</OutputDir> |
||||
</_Protobuf_OutOfDateProto> |
||||
</ItemGroup> |
||||
|
||||
<ProtoCompile Condition=" '@(_Protobuf_OutOfDateProto)' != '' " |
||||
ToolExe="$(Protobuf_ProtocFullPath)" |
||||
Generator="$(Protobuf_Generator)" |
||||
ProtoBuf="%(_Protobuf_OutOfDateProto.Source)" |
||||
ProtoPath="%(_Protobuf_OutOfDateProto.AdditionalImportDirs);$(Protobuf_StandardImportsPath);%(_Protobuf_OutOfDateProto.ProtoRoot)" |
||||
ProtoDepDir="$(Protobuf_DepFilesPath)" |
||||
OutputDir="%(_Protobuf_OutOfDateProto.OutputDir)" |
||||
OutputOptions="%(_Protobuf_OutOfDateProto._OutputOptions)" |
||||
GrpcPluginExe="%(_Protobuf_OutOfDateProto.GrpcPluginExe)" |
||||
GrpcOutputDir="%(_Protobuf_OutOfDateProto.GrpcOutputDir)" |
||||
GrpcOutputOptions="%(_Protobuf_OutOfDateProto._GrpcOutputOptions)" |
||||
> |
||||
<Output TaskParameter="GeneratedFiles" ItemName="_Protobuf_GeneratedFiles"/> |
||||
</ProtoCompile> |
||||
|
||||
<!-- Compute files expected but not in fact produced by protoc. --> |
||||
<ItemGroup Condition=" '@(_Protobuf_OutOfDateProto)' != '' "> |
||||
<Protobuf_ExpectedNotGenerated Include="@(Protobuf_ExpectedOutputs)" |
||||
Condition=" '%(Source)' != '' and '@(_Protobuf_OutOfDateProto)' != '' " /> |
||||
<Protobuf_ExpectedNotGenerated Remove="@(_Protobuf_GeneratedFiles)" /> |
||||
</ItemGroup> |
||||
</Target> |
||||
|
||||
<!-- Extension point. Plugins and/or unsupported projects may take special care of the |
||||
Protobuf_ExpectedNotGenerated list in BeforeTargets. We just silently create the |
||||
missing outputs so that out-of-date checks work (we do not add them to language |
||||
compile though). You can empty this collection in your Before targets to do nothing. |
||||
The target is not executed if the proto compiler is not executed. --> |
||||
<Target Name="Protobuf_ReconcileOutputs" |
||||
Condition=" '$(DesignTimeBuild)' != 'true' "> |
||||
<!-- Warn about unexpected/missing files outside object file directory only. |
||||
This should have happened because build was incorrectly customized. --> |
||||
<FindUnderPath Path="$(BaseIntermediateOutputPath)" Files="@(Protobuf_ExpectedNotGenerated)"> |
||||
<Output TaskParameter="InPath" ItemName="_Protobuf_ExpectedNotGeneratedInTemp"/> |
||||
<Output TaskParameter="OutOfPath" ItemName="_Protobuf_ExpectedNotGeneratedElsewhere"/> |
||||
</FindUnderPath> |
||||
|
||||
<!-- Prevent unnecessary recompilation by silently creating empty files. This probably |
||||
has happened because a proto file with an rpc service was processed by the gRPC |
||||
plugin, and the user did not set GrpcOutput to None. When we treat outputs as |
||||
transient, we can do it permissively. --> |
||||
<Touch Files="@(_Protobuf_ExpectedNotGeneratedInTemp)" AlwaysCreate="true" /> |
||||
|
||||
<!-- Also create empty files outside of the intermediate directory, if the user wants so. --> |
||||
<Touch Files="@(_Protobuf_ExpectedNotGeneratedElsewhere)" AlwaysCreate="true" |
||||
Condition=" '$(Protobuf_TouchMissingExpected)' == 'true' "/> |
||||
|
||||
<!-- You are seeing this warning because there were some Protobuf_ExpectedOutputs items |
||||
(outside of the transient directory under obj/) not in fact produced by protoc. --> |
||||
<Warning Condition=" '@(_Protobuf_ExpectedNotGeneratedElsewhere)' != '' and $(Protobuf_NoWarnMissingExpected) != 'true' " |
||||
Text="Some expected protoc outputs were not generated. @(_Protobuf_ExpectedNotGeneratedElsewhere->' %(Identity)', '')" /> |
||||
</Target> |
||||
|
||||
<!--================================================================================ |
||||
Proto cleanup |
||||
=================================================================================--> |
||||
|
||||
<!-- We fully support cleanup only in a C# project. If extending the build for other |
||||
generators/plugins, then mostly roll your own. --> |
||||
|
||||
<!-- Extension points. --> |
||||
<Target Name="Protobuf_BeforeClean" /> |
||||
<Target Name="Protobuf_AfterClean" /> |
||||
|
||||
<!-- Main cleanup sequence. --> |
||||
<Target Name="Protobuf_Clean" |
||||
Condition=" '@(ProtoBuf)' != '' " |
||||
DependsOnTargets=" Protobuf_BeforeClean; |
||||
Protobuf_PrepareClean; |
||||
_Protobuf_CoreClean; |
||||
Protobuf_AfterClean" /> |
||||
|
||||
<!-- Do proto cleanup by default in a C# project. In other types, the user should |
||||
invoke Protobuf_Clean directly if required. --> |
||||
<Target Name="_Protobuf_Clean_AfterCsClean" |
||||
AfterTargets="CoreClean" |
||||
DependsOnTargets="Protobuf_Clean" |
||||
Condition=" '$(Protobuf_ProjectSupported)' == 'true' and '$(Language)' == 'C#' " /> |
||||
|
||||
<!-- Extension point for non-C# project. ProtoCompilerOutputs is not invoked for |
||||
non-C# projects, since inferring protoc outputs is required, so this is a |
||||
no-op in other project types. In your extension target populate the |
||||
Protobuf_ExpectedOutputs with all possible output. An option is to include |
||||
all existing outputs using Include with a wildcard, if you know where to look. |
||||
|
||||
Note this is like Protobuf_PrepareCompile, but uses @(Protobuf) regardless |
||||
of the Compile metadata, to remove all possible outputs. Plugins should err |
||||
on the side of overextending the Protobuf_ExpectedOutputs here. |
||||
|
||||
All ExpectedOutputs will be removed. --> |
||||
<Target Name="Protobuf_PrepareClean" Condition=" '@(Protobuf)' != '' "> |
||||
<!-- Predict expected names. --> |
||||
<ProtoCompilerOutputs Condition=" '$(Language)' == 'C#' " |
||||
ProtoBuf="@(Protobuf)" |
||||
Generator="$(Protobuf_Generator)"> |
||||
<Output TaskParameter="PossibleOutputs" ItemName="Protobuf_ExpectedOutputs" /> |
||||
</ProtoCompilerOutputs> |
||||
</Target> |
||||
|
||||
<Target Name="_Protobuf_CoreClean"> |
||||
<ItemGroup> |
||||
<_Protobuf_Protodep Include="$(Protobuf_DepFilesPath)*.protodep" /> |
||||
</ItemGroup> |
||||
<Delete Files="@(Protobuf_ExpectedOutputs);@(_Protobuf_Protodep)" TreatErrorsAsWarnings="true" /> |
||||
</Target> |
||||
|
||||
<!--================================================================================ |
||||
Design-time support |
||||
=================================================================================--> |
||||
|
||||
<!-- Add all .proto files to the SourceFilesProjectOutputGroupOutput, so that: |
||||
* Visual Studio triggers a build when any of them changed; |
||||
* The Pack target includes .proto files into the source package. --> |
||||
<Target Name="_Protobuf_SourceFilesProjectOutputGroup" |
||||
BeforeTargets="SourceFilesProjectOutputGroup" |
||||
Condition=" '@(ProtoBuf)' != '' " > |
||||
<ItemGroup> |
||||
<SourceFilesProjectOutputGroupOutput Include="@(ProtoBuf->'%(FullPath)')" /> |
||||
</ItemGroup> |
||||
</Target> |
||||
</Project> |
@ -0,0 +1,99 @@ |
||||
<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties"> |
||||
<FileExtension Name=".proto" |
||||
ContentType="ProtoFile" /> |
||||
|
||||
<ContentType Name="ProtoFile" |
||||
DisplayName="Protocol buffer definitions file" |
||||
ItemType="ProtoBuf" /> |
||||
|
||||
<ItemType Name="ProtoBuf" |
||||
DisplayName="Protobuf compiler" /> |
||||
|
||||
<Rule Name="ProtoBuf" |
||||
DisplayName="File Properties" |
||||
PageTemplate="generic" |
||||
Description="File Properties" |
||||
OverrideMode="Extend"> |
||||
<Rule.DataSource> |
||||
<DataSource Persistence="ProjectFile" Label="Configuration" ItemType="ProtoBuf" |
||||
HasConfigurationCondition="false" SourceOfDefaultValue="AfterContext" /> |
||||
</Rule.DataSource> |
||||
|
||||
<Rule.Categories> |
||||
<Category Name="Advanced" DisplayName="Advanced" /> |
||||
<Category Name="Protobuf" DisplayName="Protobuf" /> |
||||
<Category Name="Misc" DisplayName="Misc" /> |
||||
</Rule.Categories> |
||||
|
||||
<DynamicEnumProperty Name="{}{ItemType}" DisplayName="Build Action" Category="Advanced" |
||||
Description="How the file relates to the build and deployment processes." |
||||
EnumProvider="ItemTypes" /> |
||||
|
||||
<StringProperty Name="Identity" Visible="false" ReadOnly="true"> |
||||
<StringProperty.DataSource> |
||||
<DataSource Persistence="Intrinsic" ItemType="ProtoBuf" |
||||
PersistedName="Identity" SourceOfDefaultValue="AfterContext" /> |
||||
</StringProperty.DataSource> |
||||
</StringProperty> |
||||
|
||||
<StringProperty Name="FullPath" |
||||
DisplayName="Full Path" |
||||
ReadOnly="true" |
||||
Category="Misc" |
||||
Description="Location of the file."> |
||||
<StringProperty.DataSource> |
||||
<DataSource Persistence="Intrinsic" ItemType="ProtoBuf" |
||||
PersistedName="FullPath" SourceOfDefaultValue="AfterContext" /> |
||||
</StringProperty.DataSource> |
||||
</StringProperty> |
||||
|
||||
<StringProperty Name="FileNameAndExtension" |
||||
DisplayName="File Name" |
||||
ReadOnly="true" |
||||
Category="Misc" |
||||
Description="Name of the file or folder."> |
||||
<StringProperty.DataSource> |
||||
<DataSource Persistence="Intrinsic" ItemType="ProtoBuf" |
||||
PersistedName="FileNameAndExtension" SourceOfDefaultValue="AfterContext" /> |
||||
</StringProperty.DataSource> |
||||
</StringProperty> |
||||
|
||||
<BoolProperty Name="Visible" Visible="false" Default="true" /> |
||||
|
||||
<StringProperty Name="DependentUpon" Visible="false"> |
||||
<StringProperty.Metadata> |
||||
<NameValuePair Name="DoNotCopyAcrossProjects" Value="true" /> |
||||
</StringProperty.Metadata> |
||||
</StringProperty> |
||||
|
||||
<StringProperty Name="Link" Visible="false"> |
||||
<StringProperty.DataSource> |
||||
<DataSource SourceOfDefaultValue="AfterContext" /> |
||||
</StringProperty.DataSource> |
||||
<StringProperty.Metadata> |
||||
<NameValuePair Name="DoNotCopyAcrossProjects" Value="true" /> |
||||
</StringProperty.Metadata> |
||||
</StringProperty> |
||||
|
||||
<EnumProperty Name="Access" DisplayName="Class Access" |
||||
Category="Protobuf" |
||||
Description="Public or internal access modifier on generated classes."> |
||||
<EnumValue Name="Public" DisplayName="Public" IsDefault="true" /> |
||||
<EnumValue Name="Internal" DisplayName="Internal" /> |
||||
<EnumProperty.DataSource> |
||||
<DataSource ItemType="ProtoBuf" SourceOfDefaultValue="AfterContext" |
||||
PersistenceStyle="Attribute" /> |
||||
</EnumProperty.DataSource> |
||||
</EnumProperty> |
||||
|
||||
<BoolProperty Name="ProtoCompile" DisplayName="Compile Protobuf" |
||||
Category="Protobuf" Default="true" |
||||
Description="Specifies if this file is compiled or only imported by other files."> |
||||
<BoolProperty.DataSource> |
||||
<DataSource ItemType="ProtoBuf" SourceOfDefaultValue="AfterContext" |
||||
PersistenceStyle="Attribute" /> |
||||
</BoolProperty.DataSource> |
||||
</BoolProperty> |
||||
|
||||
</Rule> |
||||
</ProjectSchemaDefinitions> |
@ -0,0 +1,17 @@ |
||||
<?xml version="1.0"?> |
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<PropertyGroup> |
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
||||
|
||||
<!-- Revision number of this package conventions (as if "API" version). --> |
||||
<Protobuf_ToolingRevision>1</Protobuf_ToolingRevision> |
||||
|
||||
<!-- For a Visual Studio C++ native project we currently only resolve tools and import paths. --> |
||||
<!-- TODO(kkm): Do not place non-tools under tools/, use build/native/bin/. --> |
||||
<!-- TODO(kkm): Do not package windows x64 builds (#13098). --> |
||||
<Protobuf_ProtocFullPath>$(MSBuildThisFileDirectory)..\..\tools\windows_x86\protoc.exe</Protobuf_ProtocFullPath> |
||||
<Protobuf_StandardImportsPath>$(MSBuildThisFileDirectory)include\</Protobuf_StandardImportsPath> |
||||
<gRPC_PluginFileName>grpc_cpp_plugin</gRPC_PluginFileName> |
||||
<gRPC_PluginFullPath>$(MSBuildThisFileDirectory)..\..\tools\windows_x86\grpc_cpp_plugin.exe</gRPC_PluginFullPath> |
||||
</PropertyGroup> |
||||
</Project> |
Loading…
Reference in new issue