fixup! Add Grpc.Tools MsBuild taks assembly, test and scripting

pull/13207/head
kkm 7 years ago
parent a93e3d2753
commit 17df1f8cf5
  1. 14
      src/csharp/Grpc.Tools.Tests/Grpc.Tools.Tests.csproj
  2. 12
      src/csharp/Grpc.Tools/Common.cs
  3. 103
      src/csharp/Grpc.Tools/DepFileUtil.cs
  4. 12
      src/csharp/Grpc.Tools/GeneratorServices.cs
  5. 31
      src/csharp/Grpc.Tools/Grpc.Tools.csproj
  6. 170
      src/csharp/Grpc.Tools/ProtoCompile.cs
  7. 2
      src/csharp/Grpc.Tools/ProtoCompilerOutputs.cs
  8. 2
      src/csharp/Grpc.Tools/ProtoReadDependencies.cs
  9. 4
      src/csharp/Grpc.Tools/build/_protobuf/Google.Protobuf.Tools.targets

@ -1,23 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\Grpc.Core\Version.csproj.include" />
<PropertyGroup>
<TargetFrameworks>netcoreapp1.0;net45</TargetFrameworks>
<TargetFrameworks>net45;netcoreapp1.0</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<Import Project="..\Grpc.Core\SourceLink.csproj.include" />
<ItemGroup>
<ProjectReference Include="..\Grpc.Tools\Grpc.Tools.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" Version="4.7.145" />
<PackageReference Include="NUnit" Version="3.9.0" />
<PackageReference Include="NUnitLite" Version="3.9.0" />
<PackageReference Include="Moq" Version="4.8.3" />
<PackageReference Include="NUnit; NUnitLite" Version="3.10.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.Build.Framework" Version="15.5.180" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.5.180" />
<PackageReference Include="Microsoft.Build.Framework; Microsoft.Build.Utilities.Core" Version="15.6.85" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">

@ -25,15 +25,15 @@ using System.Security;
[assembly: InternalsVisibleTo("Grpc.Tools.Tests")]
namespace Grpc.Tools {
// Metadata names that we refer to often.
// Metadata names (MSBuild item attributes) that we refer to often.
static class Metadata {
// On output dependency lists.
public static string kSource = "Source";
public static string Source = "Source";
// On ProtoBuf items.
public static string kProtoRoot = "ProtoRoot";
public static string kOutputDir = "OutputDir";
public static string kGrpcServices = "GrpcServices";
public static string kGrpcOutputDir = "GrpcOutputDir";
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.

@ -25,29 +25,38 @@ 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
*/
// Read file names from the dependency file to the right of ':'.
/*
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);
@ -80,7 +89,20 @@ C:\projects\foo\src\./foo.pb.h: C:/foo/include/google/protobuf/wrappers.proto\
return result.ToArray();
}
// Read file names from the dependency file to the left of ':'.
/// <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);
@ -106,10 +128,31 @@ C:\projects\foo\src\./foo.pb.h: C:/foo/include/google/protobuf/wrappers.proto\
return result.ToArray();
}
// Get complete dependency file name from directory hash and file name,
// tucked onto protoDepDir, e. g.
// ("out", "foo/file.proto") => "out/deadbeef12345678_file.protodep".
// This way, the filenames are unique but still possible to make sense of.
/// <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) {
@ -177,7 +220,7 @@ C:\projects\foo\src\./foo.pb.h: C:/foo/include/google/protobuf/wrappers.proto\
// 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 depnedencies.
// compiling, but reading it is optional when computing dependencies.
static string[] ReadDepFileLines(string filename, bool required,
TaskLoggingHelper log) {
try {
@ -189,7 +232,7 @@ C:\projects\foo\src\./foo.pb.h: C:/foo/include/google/protobuf/wrappers.proto\
if (required) {
log.LogError($"Unable to load {filename}: {ex.GetType().Name}: {ex.Message}");
} else {
log.LogMessage(MessageImportance.Low, $"Skippping {filename}: {ex.Message}");
log.LogMessage(MessageImportance.Low, $"Skipping {filename}: {ex.Message}");
}
return new string[0];
}

@ -49,7 +49,7 @@ namespace Grpc.Tools {
// 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.kGrpcServices);
string gsm = proto.GetMetadata(Metadata.GrpcServices);
return !gsm.EqualNoCase("") && !gsm.EqualNoCase("none")
&& !gsm.EqualNoCase("false");
}
@ -67,12 +67,12 @@ namespace Grpc.Tools {
Path.GetFileNameWithoutExtension(protoItem.ItemSpec));
var outputs = new string[doGrpc ? 2 : 1];
string outdir = protoItem.GetMetadata(Metadata.kOutputDir);
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.kGrpcOutputDir);
outdir = protoItem.GetMetadata(Metadata.GrpcOutputDir);
if (outdir != "") {
fileStem = Path.Combine(outdir, filename);
}
@ -105,20 +105,20 @@ namespace Grpc.Tools {
public override string[] GetPossibleOutputs(ITaskItem protoItem) {
bool doGrpc = GrpcOutputPossible(protoItem);
string root = protoItem.GetMetadata(Metadata.kProtoRoot);
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.kOutputDir);
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.kGrpcOutputDir);
outdir = protoItem.GetMetadata(Metadata.GrpcOutputDir);
if (outdir != "") {
fileStem = Path.Combine(outdir, relative, filename);
}

@ -6,7 +6,19 @@
<AssemblyName>Protobuf.MSBuild</AssemblyName>
<Version>$(GrpcCsharpVersion)</Version>
<!-- If changing targets, change also paths in Google.Protobuf.Tools.targets. -->
<TargetFrameworks>netstandard1.3;net40</TargetFrameworks>
<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'">
<!-- Workaround for 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.">
@ -32,9 +44,6 @@
</PropertyGroup>
<PropertyGroup Label="NuGet package definition" Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>../../../artifacts</PackageOutputPath>
<!-- TODO(kkm): Change to "build\" after splitting. -->
<BuildOutputTargetFolder>build\_protobuf\</BuildOutputTargetFolder>
<DevelopmentDependency>true</DevelopmentDependency>
@ -57,10 +66,9 @@ Linux and MacOS. Managed runtime is supplied separately in the Grpc.Core package
<None Pack="true" PackagePath="build\" Include="build\**\*.xml; build\**\*.props; build\**\*.targets;" />
<!-- Protobuf assets (for Google.Protobuf.Tools) -->
<_ProtoTemp Include="any.proto;api.proto;descriptor.proto;duration.proto;" />
<_ProtoTemp Include="empty.proto;field_mask.proto;source_context.proto;" />
<_ProtoTemp Include="struct.proto;timestamp.proto;type.proto;wrappers.proto" />
<_Asset PackagePath="build/native/include/google/protobuf/" Include="@(_ProtoTemp->'$(Assets_ProtoInclude)%(Identity)')" />
<_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". -->
<_Asset PackagePath="build/native/bin/windows/protoc.exe" Include="$(Assets_ProtoCompiler)windows_x86/protoc.exe" />
@ -85,10 +93,9 @@ Linux and MacOS. Managed runtime is supplied separately in the Grpc.Core package
</ItemGroup>
<ItemGroup Condition="$(_NetStandard)">
<PackageReference Include="Microsoft.Build.Framework" Version="15.5.180" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.5.180" />
<!-- Set PrivateAssets="All" on all items, so that even implicit package
dependencies do not become dependencies of this package. -->
<PackageReference Include="Microsoft.Build.Framework; Microsoft.Build.Utilities.Core" Version="15.6.85" />
<!-- 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>

@ -32,91 +32,91 @@ namespace Grpc.Tools {
/// 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.
*/
/*
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",

@ -68,7 +68,7 @@ namespace Grpc.Tools {
var outputs = generator.GetPossibleOutputs(proto);
foreach (string output in outputs) {
var ti = new TaskItem(output);
ti.SetMetadata(Metadata.kSource, proto.ItemSpec);
ti.SetMetadata(Metadata.Source, proto.ItemSpec);
possible.Add(ti);
}
}

@ -55,7 +55,7 @@ namespace Grpc.Tools {
string[] deps = DepFileUtil.ReadDependencyInputs(ProtoDepDir, proto.ItemSpec, Log);
foreach (string dep in deps) {
var ti = new TaskItem(dep);
ti.SetMetadata(Metadata.kSource, proto.ItemSpec);
ti.SetMetadata(Metadata.Source, proto.ItemSpec);
dependencies.Add(ti);
}
}

@ -7,7 +7,7 @@
<!-- 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' ">net40\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" />
@ -370,7 +370,7 @@
Design-time support
=================================================================================-->
<!-- Add all .proto files to the SourceFilesProjectOutputGroupOutput, so that
<!-- 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"

Loading…
Cancel
Save