The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
645 lines
29 KiB
645 lines
29 KiB
#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.Text; |
|
using System.Text.RegularExpressions; |
|
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" }; |
|
|
|
static readonly TimeSpan s_regexTimeout = TimeSpan.FromMilliseconds(100); |
|
|
|
static readonly List<ErrorListFilter> s_errorListFilters = new List<ErrorListFilter>() |
|
{ |
|
// Example warning with location |
|
//../Protos/greet.proto(19) : warning in column=5 : warning : When enum name is stripped and label is PascalCased (Zero), |
|
// this value label conflicts with Zero. This will make the proto fail to compile for some languages, such as C#. |
|
new ErrorListFilter |
|
{ |
|
Pattern = new Regex( |
|
pattern: "^(?'FILENAME'.+?)\\((?'LINE'\\d+)\\) ?: ?warning in column=(?'COLUMN'\\d+) ?: ?(?'TEXT'.*)", |
|
options: RegexOptions.Compiled | RegexOptions.IgnoreCase, |
|
matchTimeout: s_regexTimeout), |
|
LogAction = (log, match) => |
|
{ |
|
int.TryParse(match.Groups["LINE"].Value, out var line); |
|
int.TryParse(match.Groups["COLUMN"].Value, out var column); |
|
|
|
log.LogWarning( |
|
subcategory: null, |
|
warningCode: null, |
|
helpKeyword: null, |
|
file: match.Groups["FILENAME"].Value, |
|
lineNumber: line, |
|
columnNumber: column, |
|
endLineNumber: 0, |
|
endColumnNumber: 0, |
|
message: match.Groups["TEXT"].Value); |
|
} |
|
}, |
|
|
|
// Example error with location |
|
//../Protos/greet.proto(14) : error in column=10: "name" is already defined in "Greet.HelloRequest". |
|
new ErrorListFilter |
|
{ |
|
Pattern = new Regex( |
|
pattern: "^(?'FILENAME'.+?)\\((?'LINE'\\d+)\\) ?: ?error in column=(?'COLUMN'\\d+) ?: ?(?'TEXT'.*)", |
|
options: RegexOptions.Compiled | RegexOptions.IgnoreCase, |
|
matchTimeout: s_regexTimeout), |
|
LogAction = (log, match) => |
|
{ |
|
int.TryParse(match.Groups["LINE"].Value, out var line); |
|
int.TryParse(match.Groups["COLUMN"].Value, out var column); |
|
|
|
log.LogError( |
|
subcategory: null, |
|
errorCode: null, |
|
helpKeyword: null, |
|
file: match.Groups["FILENAME"].Value, |
|
lineNumber: line, |
|
columnNumber: column, |
|
endLineNumber: 0, |
|
endColumnNumber: 0, |
|
message: match.Groups["TEXT"].Value); |
|
} |
|
}, |
|
|
|
// Example warning without location |
|
//../Protos/greet.proto: warning: Import google/protobuf/empty.proto but not used. |
|
new ErrorListFilter |
|
{ |
|
Pattern = new Regex( |
|
pattern: "^(?'FILENAME'.+?): ?warning: ?(?'TEXT'.*)", |
|
options: RegexOptions.Compiled | RegexOptions.IgnoreCase, |
|
matchTimeout: s_regexTimeout), |
|
LogAction = (log, match) => |
|
{ |
|
log.LogWarning( |
|
subcategory: null, |
|
warningCode: null, |
|
helpKeyword: null, |
|
file: match.Groups["FILENAME"].Value, |
|
lineNumber: 0, |
|
columnNumber: 0, |
|
endLineNumber: 0, |
|
endColumnNumber: 0, |
|
message: match.Groups["TEXT"].Value); |
|
} |
|
}, |
|
|
|
// Example warning from plugins that use GOOGLE_LOG |
|
// [libprotobuf WARNING T:\altsrc\..\csharp_enum.cc:74] Duplicate enum value Work (originally Work) in PhoneType; adding underscore to distinguish |
|
new ErrorListFilter |
|
{ |
|
Pattern = new Regex( |
|
pattern: "^\\[.+? WARNING (?'FILENAME'.+?):(?'LINE'\\d+?)\\] ?(?'TEXT'.*)", |
|
options: RegexOptions.Compiled | RegexOptions.IgnoreCase, |
|
matchTimeout: s_regexTimeout), |
|
LogAction = (log, match) => |
|
{ |
|
// The filename and line logged by the plugins may not be useful to the |
|
// end user as they are not the location in the proto file but rather |
|
// in the source code for the plugin. Log them anyway as they may help in |
|
// diagnostics. |
|
int.TryParse(match.Groups["LINE"].Value, out var line); |
|
log.LogWarning( |
|
subcategory: null, |
|
warningCode: null, |
|
helpKeyword: null, |
|
file: match.Groups["FILENAME"].Value, |
|
lineNumber: line, |
|
columnNumber: 0, |
|
endLineNumber: 0, |
|
endColumnNumber: 0, |
|
message: match.Groups["TEXT"].Value); |
|
} |
|
}, |
|
|
|
// Example error from plugins that use GOOGLE_LOG |
|
// [libprotobuf ERROR T:\path\...\filename:23] Some message |
|
// [libprotobuf FATAL T:\path\...\filename:23] Some message |
|
new ErrorListFilter |
|
{ |
|
Pattern = new Regex( |
|
pattern: "^\\[.+? (?'LEVEL'ERROR|FATAL) (?'FILENAME'.+?):(?'LINE'\\d+?)\\] ?(?'TEXT'.*)", |
|
options: RegexOptions.Compiled | RegexOptions.IgnoreCase, |
|
matchTimeout: s_regexTimeout), |
|
LogAction = (log, match) => |
|
{ |
|
// The filename and line logged by the plugins may not be useful to the |
|
// end user as they are not the location in the proto file but rather |
|
// in the source code for the plugin. Log them anyway as they may help in |
|
// diagnostics. |
|
int.TryParse(match.Groups["LINE"].Value, out var line); |
|
log.LogError( |
|
subcategory: null, |
|
errorCode: null, |
|
helpKeyword: null, |
|
file: match.Groups["FILENAME"].Value, |
|
lineNumber: line, |
|
columnNumber: 0, |
|
endLineNumber: 0, |
|
endColumnNumber: 0, |
|
message: match.Groups["LEVEL"].Value + " " + match.Groups["TEXT"].Value); |
|
} |
|
}, |
|
|
|
// Example error without location |
|
//../Protos/greet.proto: Import "google/protobuf/empty.proto" was listed twice. |
|
new ErrorListFilter |
|
{ |
|
Pattern = new Regex( |
|
pattern: "^(?'FILENAME'.+?): ?(?'TEXT'.*)", |
|
options: RegexOptions.Compiled | RegexOptions.IgnoreCase, |
|
matchTimeout: s_regexTimeout), |
|
LogAction = (log, match) => |
|
{ |
|
log.LogError( |
|
subcategory: null, |
|
errorCode: null, |
|
helpKeyword: null, |
|
file: match.Groups["FILENAME"].Value, |
|
lineNumber: 0, |
|
columnNumber: 0, |
|
endLineNumber: 0, |
|
endColumnNumber: 0, |
|
message: match.Groups["TEXT"].Value); |
|
} |
|
} |
|
}; |
|
|
|
/// <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, e.g. --csharp_out). |
|
/// </summary> |
|
[Required] |
|
public string OutputDir { get; set; } |
|
|
|
/// <summary> |
|
/// Codegen options. See also OptionsFromMetadata. |
|
/// Switch: --GEN_opt= (for different generators GEN, e.g. --csharp_opt). |
|
/// </summary> |
|
public string[] OutputOptions { get; set; } |
|
|
|
/// <summary> |
|
/// Additional arguments that will be passed unmodified to protoc (and before any file names). |
|
/// For example, "--experimental_allow_proto3_optional" |
|
/// </summary> |
|
public string[] AdditionalProtocArguments { 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/protocolbuffers/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); |
|
cmd.AddSwitchMaybe("error_format", "msvs"); |
|
|
|
if (AdditionalProtocArguments != null) |
|
{ |
|
foreach (var additionalProtocOption in AdditionalProtocArguments) |
|
{ |
|
cmd.AddArg(additionalProtocOption); |
|
} |
|
} |
|
|
|
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()); |
|
} |
|
|
|
protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) |
|
{ |
|
foreach (ErrorListFilter filter in s_errorListFilters) |
|
{ |
|
Match match = filter.Pattern.Match(singleLine); |
|
|
|
if (match.Success) |
|
{ |
|
filter.LogAction(Log, match); |
|
return; |
|
} |
|
} |
|
|
|
base.LogEventsFromTextOutput(singleLine, messageImportance); |
|
} |
|
|
|
// 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; |
|
} |
|
|
|
class ErrorListFilter |
|
{ |
|
public Regex Pattern { get; set; } |
|
public Action<TaskLoggingHelper, Match> LogAction { get; set; } |
|
} |
|
}; |
|
}
|
|
|