1*cc02d7e2SAndroid Build Coastguard Worker #region Copyright notice and license 2*cc02d7e2SAndroid Build Coastguard Worker 3*cc02d7e2SAndroid Build Coastguard Worker // Copyright 2018 gRPC authors. 4*cc02d7e2SAndroid Build Coastguard Worker // 5*cc02d7e2SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 6*cc02d7e2SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 7*cc02d7e2SAndroid Build Coastguard Worker // You may obtain a copy of the License at 8*cc02d7e2SAndroid Build Coastguard Worker // 9*cc02d7e2SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0 10*cc02d7e2SAndroid Build Coastguard Worker // 11*cc02d7e2SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 12*cc02d7e2SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 13*cc02d7e2SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*cc02d7e2SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 15*cc02d7e2SAndroid Build Coastguard Worker // limitations under the License. 16*cc02d7e2SAndroid Build Coastguard Worker 17*cc02d7e2SAndroid Build Coastguard Worker #endregion 18*cc02d7e2SAndroid Build Coastguard Worker 19*cc02d7e2SAndroid Build Coastguard Worker using System; 20*cc02d7e2SAndroid Build Coastguard Worker using System.Collections.Generic; 21*cc02d7e2SAndroid Build Coastguard Worker using System.Text; 22*cc02d7e2SAndroid Build Coastguard Worker using System.Text.RegularExpressions; 23*cc02d7e2SAndroid Build Coastguard Worker using Microsoft.Build.Framework; 24*cc02d7e2SAndroid Build Coastguard Worker using Microsoft.Build.Utilities; 25*cc02d7e2SAndroid Build Coastguard Worker 26*cc02d7e2SAndroid Build Coastguard Worker namespace Grpc.Tools 27*cc02d7e2SAndroid Build Coastguard Worker { 28*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 29*cc02d7e2SAndroid Build Coastguard Worker /// Run Google proto compiler (protoc). 30*cc02d7e2SAndroid Build Coastguard Worker /// 31*cc02d7e2SAndroid Build Coastguard Worker /// After a successful run, the task reads the dependency file if specified 32*cc02d7e2SAndroid Build Coastguard Worker /// to be saved by the compiler, and returns its output files. 33*cc02d7e2SAndroid Build Coastguard Worker /// 34*cc02d7e2SAndroid Build Coastguard Worker /// This task (unlike PrepareProtoCompile) does not attempt to guess anything 35*cc02d7e2SAndroid Build Coastguard Worker /// about language-specific behavior of protoc, and therefore can be used for 36*cc02d7e2SAndroid Build Coastguard Worker /// any language outputs. 37*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 38*cc02d7e2SAndroid Build Coastguard Worker public class ProtoCompile : ToolTask 39*cc02d7e2SAndroid Build Coastguard Worker { 40*cc02d7e2SAndroid Build Coastguard Worker /* 41*cc02d7e2SAndroid Build Coastguard Worker 42*cc02d7e2SAndroid Build Coastguard Worker Usage: /home/kkm/work/protobuf/src/.libs/lt-protoc [OPTION] PROTO_FILES 43*cc02d7e2SAndroid Build Coastguard Worker Parse PROTO_FILES and generate output based on the options given: 44*cc02d7e2SAndroid Build Coastguard Worker -IPATH, --proto_path=PATH Specify the directory in which to search for 45*cc02d7e2SAndroid Build Coastguard Worker imports. May be specified multiple times; 46*cc02d7e2SAndroid Build Coastguard Worker directories will be searched in order. If not 47*cc02d7e2SAndroid Build Coastguard Worker given, the current working directory is used. 48*cc02d7e2SAndroid Build Coastguard Worker --version Show version info and exit. 49*cc02d7e2SAndroid Build Coastguard Worker -h, --help Show this text and exit. 50*cc02d7e2SAndroid Build Coastguard Worker --encode=MESSAGE_TYPE Read a text-format message of the given type 51*cc02d7e2SAndroid Build Coastguard Worker from standard input and write it in binary 52*cc02d7e2SAndroid Build Coastguard Worker to standard output. The message type must 53*cc02d7e2SAndroid Build Coastguard Worker be defined in PROTO_FILES or their imports. 54*cc02d7e2SAndroid Build Coastguard Worker --decode=MESSAGE_TYPE Read a binary message of the given type from 55*cc02d7e2SAndroid Build Coastguard Worker standard input and write it in text format 56*cc02d7e2SAndroid Build Coastguard Worker to standard output. The message type must 57*cc02d7e2SAndroid Build Coastguard Worker be defined in PROTO_FILES or their imports. 58*cc02d7e2SAndroid Build Coastguard Worker --decode_raw Read an arbitrary protocol message from 59*cc02d7e2SAndroid Build Coastguard Worker standard input and write the raw tag/value 60*cc02d7e2SAndroid Build Coastguard Worker pairs in text format to standard output. No 61*cc02d7e2SAndroid Build Coastguard Worker PROTO_FILES should be given when using this 62*cc02d7e2SAndroid Build Coastguard Worker flag. 63*cc02d7e2SAndroid Build Coastguard Worker --descriptor_set_in=FILES Specifies a delimited list of FILES 64*cc02d7e2SAndroid Build Coastguard Worker each containing a FileDescriptorSet (a 65*cc02d7e2SAndroid Build Coastguard Worker protocol buffer defined in descriptor.proto). 66*cc02d7e2SAndroid Build Coastguard Worker The FileDescriptor for each of the PROTO_FILES 67*cc02d7e2SAndroid Build Coastguard Worker provided will be loaded from these 68*cc02d7e2SAndroid Build Coastguard Worker FileDescriptorSets. If a FileDescriptor 69*cc02d7e2SAndroid Build Coastguard Worker appears multiple times, the first occurrence 70*cc02d7e2SAndroid Build Coastguard Worker will be used. 71*cc02d7e2SAndroid Build Coastguard Worker -oFILE, Writes a FileDescriptorSet (a protocol buffer, 72*cc02d7e2SAndroid Build Coastguard Worker --descriptor_set_out=FILE defined in descriptor.proto) containing all of 73*cc02d7e2SAndroid Build Coastguard Worker the input files to FILE. 74*cc02d7e2SAndroid Build Coastguard Worker --include_imports When using --descriptor_set_out, also include 75*cc02d7e2SAndroid Build Coastguard Worker all dependencies of the input files in the 76*cc02d7e2SAndroid Build Coastguard Worker set, so that the set is self-contained. 77*cc02d7e2SAndroid Build Coastguard Worker --include_source_info When using --descriptor_set_out, do not strip 78*cc02d7e2SAndroid Build Coastguard Worker SourceCodeInfo from the FileDescriptorProto. 79*cc02d7e2SAndroid Build Coastguard Worker This results in vastly larger descriptors that 80*cc02d7e2SAndroid Build Coastguard Worker include information about the original 81*cc02d7e2SAndroid Build Coastguard Worker location of each decl in the source file as 82*cc02d7e2SAndroid Build Coastguard Worker well as surrounding comments. 83*cc02d7e2SAndroid Build Coastguard Worker --dependency_out=FILE Write a dependency output file in the format 84*cc02d7e2SAndroid Build Coastguard Worker expected by make. This writes the transitive 85*cc02d7e2SAndroid Build Coastguard Worker set of input file paths to FILE 86*cc02d7e2SAndroid Build Coastguard Worker --error_format=FORMAT Set the format in which to print errors. 87*cc02d7e2SAndroid Build Coastguard Worker FORMAT may be 'gcc' (the default) or 'msvs' 88*cc02d7e2SAndroid Build Coastguard Worker (Microsoft Visual Studio format). 89*cc02d7e2SAndroid Build Coastguard Worker --print_free_field_numbers Print the free field numbers of the messages 90*cc02d7e2SAndroid Build Coastguard Worker defined in the given proto files. Groups share 91*cc02d7e2SAndroid Build Coastguard Worker the same field number space with the parent 92*cc02d7e2SAndroid Build Coastguard Worker message. Extension ranges are counted as 93*cc02d7e2SAndroid Build Coastguard Worker occupied fields numbers. 94*cc02d7e2SAndroid Build Coastguard Worker 95*cc02d7e2SAndroid Build Coastguard Worker --plugin=EXECUTABLE Specifies a plugin executable to use. 96*cc02d7e2SAndroid Build Coastguard Worker Normally, protoc searches the PATH for 97*cc02d7e2SAndroid Build Coastguard Worker plugins, but you may specify additional 98*cc02d7e2SAndroid Build Coastguard Worker executables not in the path using this flag. 99*cc02d7e2SAndroid Build Coastguard Worker Additionally, EXECUTABLE may be of the form 100*cc02d7e2SAndroid Build Coastguard Worker NAME=PATH, in which case the given plugin name 101*cc02d7e2SAndroid Build Coastguard Worker is mapped to the given executable even if 102*cc02d7e2SAndroid Build Coastguard Worker the executable's own name differs. 103*cc02d7e2SAndroid Build Coastguard Worker --cpp_out=OUT_DIR Generate C++ header and source. 104*cc02d7e2SAndroid Build Coastguard Worker --csharp_out=OUT_DIR Generate C# source file. 105*cc02d7e2SAndroid Build Coastguard Worker --java_out=OUT_DIR Generate Java source file. 106*cc02d7e2SAndroid Build Coastguard Worker --javanano_out=OUT_DIR Generate Java Nano source file. 107*cc02d7e2SAndroid Build Coastguard Worker --js_out=OUT_DIR Generate JavaScript source. 108*cc02d7e2SAndroid Build Coastguard Worker --objc_out=OUT_DIR Generate Objective C header and source. 109*cc02d7e2SAndroid Build Coastguard Worker --php_out=OUT_DIR Generate PHP source file. 110*cc02d7e2SAndroid Build Coastguard Worker --python_out=OUT_DIR Generate Python source file. 111*cc02d7e2SAndroid Build Coastguard Worker --ruby_out=OUT_DIR Generate Ruby source file. 112*cc02d7e2SAndroid Build Coastguard Worker @<filename> Read options and filenames from file. If a 113*cc02d7e2SAndroid Build Coastguard Worker relative file path is specified, the file 114*cc02d7e2SAndroid Build Coastguard Worker will be searched in the working directory. 115*cc02d7e2SAndroid Build Coastguard Worker The --proto_path option will not affect how 116*cc02d7e2SAndroid Build Coastguard Worker this argument file is searched. Content of 117*cc02d7e2SAndroid Build Coastguard Worker the file will be expanded in the position of 118*cc02d7e2SAndroid Build Coastguard Worker @<filename> as in the argument list. Note 119*cc02d7e2SAndroid Build Coastguard Worker that shell expansion is not applied to the 120*cc02d7e2SAndroid Build Coastguard Worker content of the file (i.e., you cannot use 121*cc02d7e2SAndroid Build Coastguard Worker quotes, wildcards, escapes, commands, etc.). 122*cc02d7e2SAndroid Build Coastguard Worker Each line corresponds to a single argument, 123*cc02d7e2SAndroid Build Coastguard Worker even if it contains spaces. 124*cc02d7e2SAndroid Build Coastguard Worker */ 125*cc02d7e2SAndroid Build Coastguard Worker static string[] s_supportedGenerators = new[] { "cpp", "csharp", "java", 126*cc02d7e2SAndroid Build Coastguard Worker "javanano", "js", "objc", 127*cc02d7e2SAndroid Build Coastguard Worker "php", "python", "ruby" }; 128*cc02d7e2SAndroid Build Coastguard Worker 129*cc02d7e2SAndroid Build Coastguard Worker static readonly TimeSpan s_regexTimeout = TimeSpan.FromSeconds(1); 130*cc02d7e2SAndroid Build Coastguard Worker 131*cc02d7e2SAndroid Build Coastguard Worker static readonly List<ErrorListFilter> s_errorListFilters = new List<ErrorListFilter>() 132*cc02d7e2SAndroid Build Coastguard Worker { 133*cc02d7e2SAndroid Build Coastguard Worker // Example warning with location 134*cc02d7e2SAndroid Build Coastguard Worker //../Protos/greet.proto(19) : warning in column=5 : warning : When enum name is stripped and label is PascalCased (Zero), 135*cc02d7e2SAndroid Build Coastguard Worker // this value label conflicts with Zero. This will make the proto fail to compile for some languages, such as C#. 136*cc02d7e2SAndroid Build Coastguard Worker new ErrorListFilter 137*cc02d7e2SAndroid Build Coastguard Worker { 138*cc02d7e2SAndroid Build Coastguard Worker Pattern = new Regex( 139*cc02d7e2SAndroid Build Coastguard Worker pattern: "^(?'FILENAME'.+?)\\((?'LINE'\\d+)\\) ?: ?warning in column=(?'COLUMN'\\d+) ?: ?(?'TEXT'.*)", 140*cc02d7e2SAndroid Build Coastguard Worker options: RegexOptions.Compiled | RegexOptions.IgnoreCase, 141*cc02d7e2SAndroid Build Coastguard Worker matchTimeout: s_regexTimeout), 142*cc02d7e2SAndroid Build Coastguard Worker LogAction = (log, match) => 143*cc02d7e2SAndroid Build Coastguard Worker { 144*cc02d7e2SAndroid Build Coastguard Worker int.TryParse(match.Groups["LINE"].Value, out var line); 145*cc02d7e2SAndroid Build Coastguard Worker int.TryParse(match.Groups["COLUMN"].Value, out var column); 146*cc02d7e2SAndroid Build Coastguard Worker 147*cc02d7e2SAndroid Build Coastguard Worker log.LogWarning( 148*cc02d7e2SAndroid Build Coastguard Worker subcategory: null, 149*cc02d7e2SAndroid Build Coastguard Worker warningCode: null, 150*cc02d7e2SAndroid Build Coastguard Worker helpKeyword: null, 151*cc02d7e2SAndroid Build Coastguard Worker file: match.Groups["FILENAME"].Value, 152*cc02d7e2SAndroid Build Coastguard Worker lineNumber: line, 153*cc02d7e2SAndroid Build Coastguard Worker columnNumber: column, 154*cc02d7e2SAndroid Build Coastguard Worker endLineNumber: 0, 155*cc02d7e2SAndroid Build Coastguard Worker endColumnNumber: 0, 156*cc02d7e2SAndroid Build Coastguard Worker message: match.Groups["TEXT"].Value); 157*cc02d7e2SAndroid Build Coastguard Worker } 158*cc02d7e2SAndroid Build Coastguard Worker }, 159*cc02d7e2SAndroid Build Coastguard Worker 160*cc02d7e2SAndroid Build Coastguard Worker // Example error with location 161*cc02d7e2SAndroid Build Coastguard Worker //../Protos/greet.proto(14) : error in column=10: "name" is already defined in "Greet.HelloRequest". 162*cc02d7e2SAndroid Build Coastguard Worker new ErrorListFilter 163*cc02d7e2SAndroid Build Coastguard Worker { 164*cc02d7e2SAndroid Build Coastguard Worker Pattern = new Regex( 165*cc02d7e2SAndroid Build Coastguard Worker pattern: "^(?'FILENAME'.+?)\\((?'LINE'\\d+)\\) ?: ?error in column=(?'COLUMN'\\d+) ?: ?(?'TEXT'.*)", 166*cc02d7e2SAndroid Build Coastguard Worker options: RegexOptions.Compiled | RegexOptions.IgnoreCase, 167*cc02d7e2SAndroid Build Coastguard Worker matchTimeout: s_regexTimeout), 168*cc02d7e2SAndroid Build Coastguard Worker LogAction = (log, match) => 169*cc02d7e2SAndroid Build Coastguard Worker { 170*cc02d7e2SAndroid Build Coastguard Worker int.TryParse(match.Groups["LINE"].Value, out var line); 171*cc02d7e2SAndroid Build Coastguard Worker int.TryParse(match.Groups["COLUMN"].Value, out var column); 172*cc02d7e2SAndroid Build Coastguard Worker 173*cc02d7e2SAndroid Build Coastguard Worker log.LogError( 174*cc02d7e2SAndroid Build Coastguard Worker subcategory: null, 175*cc02d7e2SAndroid Build Coastguard Worker errorCode: null, 176*cc02d7e2SAndroid Build Coastguard Worker helpKeyword: null, 177*cc02d7e2SAndroid Build Coastguard Worker file: match.Groups["FILENAME"].Value, 178*cc02d7e2SAndroid Build Coastguard Worker lineNumber: line, 179*cc02d7e2SAndroid Build Coastguard Worker columnNumber: column, 180*cc02d7e2SAndroid Build Coastguard Worker endLineNumber: 0, 181*cc02d7e2SAndroid Build Coastguard Worker endColumnNumber: 0, 182*cc02d7e2SAndroid Build Coastguard Worker message: match.Groups["TEXT"].Value); 183*cc02d7e2SAndroid Build Coastguard Worker } 184*cc02d7e2SAndroid Build Coastguard Worker }, 185*cc02d7e2SAndroid Build Coastguard Worker 186*cc02d7e2SAndroid Build Coastguard Worker // Example warning without location 187*cc02d7e2SAndroid Build Coastguard Worker //../Protos/greet.proto: warning: Import google/protobuf/empty.proto but not used. 188*cc02d7e2SAndroid Build Coastguard Worker new ErrorListFilter 189*cc02d7e2SAndroid Build Coastguard Worker { 190*cc02d7e2SAndroid Build Coastguard Worker Pattern = new Regex( 191*cc02d7e2SAndroid Build Coastguard Worker pattern: "^(?'FILENAME'.+?): ?warning: ?(?'TEXT'.*)", 192*cc02d7e2SAndroid Build Coastguard Worker options: RegexOptions.Compiled | RegexOptions.IgnoreCase, 193*cc02d7e2SAndroid Build Coastguard Worker matchTimeout: s_regexTimeout), 194*cc02d7e2SAndroid Build Coastguard Worker LogAction = (log, match) => 195*cc02d7e2SAndroid Build Coastguard Worker { 196*cc02d7e2SAndroid Build Coastguard Worker log.LogWarning( 197*cc02d7e2SAndroid Build Coastguard Worker subcategory: null, 198*cc02d7e2SAndroid Build Coastguard Worker warningCode: null, 199*cc02d7e2SAndroid Build Coastguard Worker helpKeyword: null, 200*cc02d7e2SAndroid Build Coastguard Worker file: match.Groups["FILENAME"].Value, 201*cc02d7e2SAndroid Build Coastguard Worker lineNumber: 0, 202*cc02d7e2SAndroid Build Coastguard Worker columnNumber: 0, 203*cc02d7e2SAndroid Build Coastguard Worker endLineNumber: 0, 204*cc02d7e2SAndroid Build Coastguard Worker endColumnNumber: 0, 205*cc02d7e2SAndroid Build Coastguard Worker message: match.Groups["TEXT"].Value); 206*cc02d7e2SAndroid Build Coastguard Worker } 207*cc02d7e2SAndroid Build Coastguard Worker }, 208*cc02d7e2SAndroid Build Coastguard Worker 209*cc02d7e2SAndroid Build Coastguard Worker // Example warning from plugins that use GOOGLE_LOG 210*cc02d7e2SAndroid Build Coastguard Worker // [libprotobuf WARNING T:\altsrc\..\csharp_enum.cc:74] Duplicate enum value Work (originally Work) in PhoneType; adding underscore to distinguish 211*cc02d7e2SAndroid Build Coastguard Worker new ErrorListFilter 212*cc02d7e2SAndroid Build Coastguard Worker { 213*cc02d7e2SAndroid Build Coastguard Worker Pattern = new Regex( 214*cc02d7e2SAndroid Build Coastguard Worker pattern: "^\\[.+? WARNING (?'FILENAME'.+?):(?'LINE'\\d+?)\\] ?(?'TEXT'.*)", 215*cc02d7e2SAndroid Build Coastguard Worker options: RegexOptions.Compiled | RegexOptions.IgnoreCase, 216*cc02d7e2SAndroid Build Coastguard Worker matchTimeout: s_regexTimeout), 217*cc02d7e2SAndroid Build Coastguard Worker LogAction = (log, match) => 218*cc02d7e2SAndroid Build Coastguard Worker { 219*cc02d7e2SAndroid Build Coastguard Worker // The filename and line logged by the plugins may not be useful to the 220*cc02d7e2SAndroid Build Coastguard Worker // end user as they are not the location in the proto file but rather 221*cc02d7e2SAndroid Build Coastguard Worker // in the source code for the plugin. Log them anyway as they may help in 222*cc02d7e2SAndroid Build Coastguard Worker // diagnostics. 223*cc02d7e2SAndroid Build Coastguard Worker int.TryParse(match.Groups["LINE"].Value, out var line); 224*cc02d7e2SAndroid Build Coastguard Worker log.LogWarning( 225*cc02d7e2SAndroid Build Coastguard Worker subcategory: null, 226*cc02d7e2SAndroid Build Coastguard Worker warningCode: null, 227*cc02d7e2SAndroid Build Coastguard Worker helpKeyword: null, 228*cc02d7e2SAndroid Build Coastguard Worker file: match.Groups["FILENAME"].Value, 229*cc02d7e2SAndroid Build Coastguard Worker lineNumber: line, 230*cc02d7e2SAndroid Build Coastguard Worker columnNumber: 0, 231*cc02d7e2SAndroid Build Coastguard Worker endLineNumber: 0, 232*cc02d7e2SAndroid Build Coastguard Worker endColumnNumber: 0, 233*cc02d7e2SAndroid Build Coastguard Worker message: match.Groups["TEXT"].Value); 234*cc02d7e2SAndroid Build Coastguard Worker } 235*cc02d7e2SAndroid Build Coastguard Worker }, 236*cc02d7e2SAndroid Build Coastguard Worker 237*cc02d7e2SAndroid Build Coastguard Worker // Example error from plugins that use GOOGLE_LOG 238*cc02d7e2SAndroid Build Coastguard Worker // [libprotobuf ERROR T:\path\...\filename:23] Some message 239*cc02d7e2SAndroid Build Coastguard Worker // [libprotobuf FATAL T:\path\...\filename:23] Some message 240*cc02d7e2SAndroid Build Coastguard Worker new ErrorListFilter 241*cc02d7e2SAndroid Build Coastguard Worker { 242*cc02d7e2SAndroid Build Coastguard Worker Pattern = new Regex( 243*cc02d7e2SAndroid Build Coastguard Worker pattern: "^\\[.+? (?'LEVEL'ERROR|FATAL) (?'FILENAME'.+?):(?'LINE'\\d+?)\\] ?(?'TEXT'.*)", 244*cc02d7e2SAndroid Build Coastguard Worker options: RegexOptions.Compiled | RegexOptions.IgnoreCase, 245*cc02d7e2SAndroid Build Coastguard Worker matchTimeout: s_regexTimeout), 246*cc02d7e2SAndroid Build Coastguard Worker LogAction = (log, match) => 247*cc02d7e2SAndroid Build Coastguard Worker { 248*cc02d7e2SAndroid Build Coastguard Worker // The filename and line logged by the plugins may not be useful to the 249*cc02d7e2SAndroid Build Coastguard Worker // end user as they are not the location in the proto file but rather 250*cc02d7e2SAndroid Build Coastguard Worker // in the source code for the plugin. Log them anyway as they may help in 251*cc02d7e2SAndroid Build Coastguard Worker // diagnostics. 252*cc02d7e2SAndroid Build Coastguard Worker int.TryParse(match.Groups["LINE"].Value, out var line); 253*cc02d7e2SAndroid Build Coastguard Worker log.LogError( 254*cc02d7e2SAndroid Build Coastguard Worker subcategory: null, 255*cc02d7e2SAndroid Build Coastguard Worker errorCode: null, 256*cc02d7e2SAndroid Build Coastguard Worker helpKeyword: null, 257*cc02d7e2SAndroid Build Coastguard Worker file: match.Groups["FILENAME"].Value, 258*cc02d7e2SAndroid Build Coastguard Worker lineNumber: line, 259*cc02d7e2SAndroid Build Coastguard Worker columnNumber: 0, 260*cc02d7e2SAndroid Build Coastguard Worker endLineNumber: 0, 261*cc02d7e2SAndroid Build Coastguard Worker endColumnNumber: 0, 262*cc02d7e2SAndroid Build Coastguard Worker message: match.Groups["LEVEL"].Value + " " + match.Groups["TEXT"].Value); 263*cc02d7e2SAndroid Build Coastguard Worker } 264*cc02d7e2SAndroid Build Coastguard Worker }, 265*cc02d7e2SAndroid Build Coastguard Worker 266*cc02d7e2SAndroid Build Coastguard Worker // Example error without location 267*cc02d7e2SAndroid Build Coastguard Worker //../Protos/greet.proto: Import "google/protobuf/empty.proto" was listed twice. 268*cc02d7e2SAndroid Build Coastguard Worker new ErrorListFilter 269*cc02d7e2SAndroid Build Coastguard Worker { 270*cc02d7e2SAndroid Build Coastguard Worker Pattern = new Regex( 271*cc02d7e2SAndroid Build Coastguard Worker pattern: "^(?'FILENAME'.+?): ?(?'TEXT'.*)", 272*cc02d7e2SAndroid Build Coastguard Worker options: RegexOptions.Compiled | RegexOptions.IgnoreCase, 273*cc02d7e2SAndroid Build Coastguard Worker matchTimeout: s_regexTimeout), 274*cc02d7e2SAndroid Build Coastguard Worker LogAction = (log, match) => 275*cc02d7e2SAndroid Build Coastguard Worker { 276*cc02d7e2SAndroid Build Coastguard Worker log.LogError( 277*cc02d7e2SAndroid Build Coastguard Worker subcategory: null, 278*cc02d7e2SAndroid Build Coastguard Worker errorCode: null, 279*cc02d7e2SAndroid Build Coastguard Worker helpKeyword: null, 280*cc02d7e2SAndroid Build Coastguard Worker file: match.Groups["FILENAME"].Value, 281*cc02d7e2SAndroid Build Coastguard Worker lineNumber: 0, 282*cc02d7e2SAndroid Build Coastguard Worker columnNumber: 0, 283*cc02d7e2SAndroid Build Coastguard Worker endLineNumber: 0, 284*cc02d7e2SAndroid Build Coastguard Worker endColumnNumber: 0, 285*cc02d7e2SAndroid Build Coastguard Worker message: match.Groups["TEXT"].Value); 286*cc02d7e2SAndroid Build Coastguard Worker } 287*cc02d7e2SAndroid Build Coastguard Worker } 288*cc02d7e2SAndroid Build Coastguard Worker }; 289*cc02d7e2SAndroid Build Coastguard Worker 290*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 291*cc02d7e2SAndroid Build Coastguard Worker /// Code generator. 292*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 293*cc02d7e2SAndroid Build Coastguard Worker [Required] 294*cc02d7e2SAndroid Build Coastguard Worker public string Generator { get; set; } 295*cc02d7e2SAndroid Build Coastguard Worker 296*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 297*cc02d7e2SAndroid Build Coastguard Worker /// Protobuf files to compile. 298*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 299*cc02d7e2SAndroid Build Coastguard Worker [Required] 300*cc02d7e2SAndroid Build Coastguard Worker public ITaskItem[] Protobuf { get; set; } 301*cc02d7e2SAndroid Build Coastguard Worker 302*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 303*cc02d7e2SAndroid Build Coastguard Worker /// Directory where protoc dependency files are cached. If provided, dependency 304*cc02d7e2SAndroid Build Coastguard Worker /// output filename is autogenerated from source directory hash and file name. 305*cc02d7e2SAndroid Build Coastguard Worker /// Mutually exclusive with DependencyOut. 306*cc02d7e2SAndroid Build Coastguard Worker /// Switch: --dependency_out (with autogenerated file name). 307*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 308*cc02d7e2SAndroid Build Coastguard Worker public string ProtoDepDir { get; set; } 309*cc02d7e2SAndroid Build Coastguard Worker 310*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 311*cc02d7e2SAndroid Build Coastguard Worker /// Dependency file full name. Mutually exclusive with ProtoDepDir. 312*cc02d7e2SAndroid Build Coastguard Worker /// Autogenerated file name is available in this property after execution. 313*cc02d7e2SAndroid Build Coastguard Worker /// Switch: --dependency_out. 314*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 315*cc02d7e2SAndroid Build Coastguard Worker [Output] 316*cc02d7e2SAndroid Build Coastguard Worker public string DependencyOut { get; set; } 317*cc02d7e2SAndroid Build Coastguard Worker 318*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 319*cc02d7e2SAndroid Build Coastguard Worker /// The directories to search for imports. Directories will be searched 320*cc02d7e2SAndroid Build Coastguard Worker /// in order. If not given, the current working directory is used. 321*cc02d7e2SAndroid Build Coastguard Worker /// Switch: --proto_path. 322*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 323*cc02d7e2SAndroid Build Coastguard Worker public string[] ProtoPath { get; set; } 324*cc02d7e2SAndroid Build Coastguard Worker 325*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 326*cc02d7e2SAndroid Build Coastguard Worker /// Generated code directory. The generator property determines the language. 327*cc02d7e2SAndroid Build Coastguard Worker /// Switch: --GEN_out= (for different generators GEN, e.g. --csharp_out). 328*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 329*cc02d7e2SAndroid Build Coastguard Worker [Required] 330*cc02d7e2SAndroid Build Coastguard Worker public string OutputDir { get; set; } 331*cc02d7e2SAndroid Build Coastguard Worker 332*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 333*cc02d7e2SAndroid Build Coastguard Worker /// Codegen options. See also OptionsFromMetadata. 334*cc02d7e2SAndroid Build Coastguard Worker /// Switch: --GEN_opt= (for different generators GEN, e.g. --csharp_opt). 335*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 336*cc02d7e2SAndroid Build Coastguard Worker public string[] OutputOptions { get; set; } 337*cc02d7e2SAndroid Build Coastguard Worker 338*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 339*cc02d7e2SAndroid Build Coastguard Worker /// Additional arguments that will be passed unmodified to protoc (and before any file names). 340*cc02d7e2SAndroid Build Coastguard Worker /// For example, "--experimental_allow_proto3_optional" 341*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 342*cc02d7e2SAndroid Build Coastguard Worker public string[] AdditionalProtocArguments { get; set; } 343*cc02d7e2SAndroid Build Coastguard Worker 344*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 345*cc02d7e2SAndroid Build Coastguard Worker /// Full path to the gRPC plugin executable. If specified, gRPC generation 346*cc02d7e2SAndroid Build Coastguard Worker /// is enabled for the files. 347*cc02d7e2SAndroid Build Coastguard Worker /// Switch: --plugin=protoc-gen-grpc= 348*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 349*cc02d7e2SAndroid Build Coastguard Worker public string GrpcPluginExe { get; set; } 350*cc02d7e2SAndroid Build Coastguard Worker 351*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 352*cc02d7e2SAndroid Build Coastguard Worker /// Generated gRPC directory. The generator property determines the 353*cc02d7e2SAndroid Build Coastguard Worker /// language. If gRPC is enabled but this is not given, OutputDir is used. 354*cc02d7e2SAndroid Build Coastguard Worker /// Switch: --grpc_out= 355*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 356*cc02d7e2SAndroid Build Coastguard Worker public string GrpcOutputDir { get; set; } 357*cc02d7e2SAndroid Build Coastguard Worker 358*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 359*cc02d7e2SAndroid Build Coastguard Worker /// gRPC Codegen options. See also OptionsFromMetadata. 360*cc02d7e2SAndroid Build Coastguard Worker /// --grpc_opt=opt1,opt2=val (comma-separated). 361*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 362*cc02d7e2SAndroid Build Coastguard Worker public string[] GrpcOutputOptions { get; set; } 363*cc02d7e2SAndroid Build Coastguard Worker 364*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 365*cc02d7e2SAndroid Build Coastguard Worker /// List of files written in addition to generated outputs. Includes a 366*cc02d7e2SAndroid Build Coastguard Worker /// single item for the dependency file if written. 367*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 368*cc02d7e2SAndroid Build Coastguard Worker [Output] 369*cc02d7e2SAndroid Build Coastguard Worker public ITaskItem[] AdditionalFileWrites { get; private set; } 370*cc02d7e2SAndroid Build Coastguard Worker 371*cc02d7e2SAndroid Build Coastguard Worker /// <summary> 372*cc02d7e2SAndroid Build Coastguard Worker /// List of language files generated by protoc. Empty unless DependencyOut 373*cc02d7e2SAndroid Build Coastguard Worker /// or ProtoDepDir is set, since the file writes are extracted from protoc 374*cc02d7e2SAndroid Build Coastguard Worker /// dependency output file. 375*cc02d7e2SAndroid Build Coastguard Worker /// </summary> 376*cc02d7e2SAndroid Build Coastguard Worker [Output] 377*cc02d7e2SAndroid Build Coastguard Worker public ITaskItem[] GeneratedFiles { get; private set; } 378*cc02d7e2SAndroid Build Coastguard Worker 379*cc02d7e2SAndroid Build Coastguard Worker // Hide this property from MSBuild, we should never use a shell script. 380*cc02d7e2SAndroid Build Coastguard Worker private new bool UseCommandProcessor { get; set; } 381*cc02d7e2SAndroid Build Coastguard Worker 382*cc02d7e2SAndroid Build Coastguard Worker protected override string ToolName => Platform.IsWindows ? "protoc.exe" : "protoc"; 383*cc02d7e2SAndroid Build Coastguard Worker 384*cc02d7e2SAndroid Build Coastguard Worker // Since we never try to really locate protoc.exe somehow, just try ToolExe 385*cc02d7e2SAndroid Build Coastguard Worker // as the full tool location. It will be either just protoc[.exe] from 386*cc02d7e2SAndroid Build Coastguard Worker // ToolName above if not set by the user, or a user-supplied full path. The 387*cc02d7e2SAndroid Build Coastguard Worker // base class will then resolve the former using system PATH. GenerateFullPathToTool()388*cc02d7e2SAndroid Build Coastguard Worker protected override string GenerateFullPathToTool() => ToolExe; 389*cc02d7e2SAndroid Build Coastguard Worker 390*cc02d7e2SAndroid Build Coastguard Worker // Log protoc errors with the High priority (bold white in MsBuild, 391*cc02d7e2SAndroid Build Coastguard Worker // printed with -v:n, and shown in the Output windows in VS). 392*cc02d7e2SAndroid Build Coastguard Worker protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High; 393*cc02d7e2SAndroid Build Coastguard Worker 394*cc02d7e2SAndroid Build Coastguard Worker // Called by base class to validate arguments and make them consistent. ValidateParameters()395*cc02d7e2SAndroid Build Coastguard Worker protected override bool ValidateParameters() 396*cc02d7e2SAndroid Build Coastguard Worker { 397*cc02d7e2SAndroid Build Coastguard Worker // Part of proto command line switches, must be lowercased. 398*cc02d7e2SAndroid Build Coastguard Worker Generator = Generator.ToLowerInvariant(); 399*cc02d7e2SAndroid Build Coastguard Worker if (!System.Array.Exists(s_supportedGenerators, g => g == Generator)) 400*cc02d7e2SAndroid Build Coastguard Worker { 401*cc02d7e2SAndroid Build Coastguard Worker Log.LogError("Invalid value for Generator='{0}'. Supported generators: {1}", 402*cc02d7e2SAndroid Build Coastguard Worker Generator, string.Join(", ", s_supportedGenerators)); 403*cc02d7e2SAndroid Build Coastguard Worker } 404*cc02d7e2SAndroid Build Coastguard Worker 405*cc02d7e2SAndroid Build Coastguard Worker if (ProtoDepDir != null && DependencyOut != null) 406*cc02d7e2SAndroid Build Coastguard Worker { 407*cc02d7e2SAndroid Build Coastguard Worker Log.LogError("Properties ProtoDepDir and DependencyOut may not be both specified"); 408*cc02d7e2SAndroid Build Coastguard Worker } 409*cc02d7e2SAndroid Build Coastguard Worker 410*cc02d7e2SAndroid Build Coastguard Worker if (Protobuf.Length > 1 && (ProtoDepDir != null || DependencyOut != null)) 411*cc02d7e2SAndroid Build Coastguard Worker { 412*cc02d7e2SAndroid Build Coastguard Worker Log.LogError("Proto compiler currently allows only one input when " + 413*cc02d7e2SAndroid Build Coastguard Worker "--dependency_out is specified (via ProtoDepDir or DependencyOut). " + 414*cc02d7e2SAndroid Build Coastguard Worker "Tracking issue: https://github.com/protocolbuffers/protobuf/pull/3959"); 415*cc02d7e2SAndroid Build Coastguard Worker } 416*cc02d7e2SAndroid Build Coastguard Worker 417*cc02d7e2SAndroid Build Coastguard Worker // Use ProtoDepDir to autogenerate DependencyOut 418*cc02d7e2SAndroid Build Coastguard Worker if (ProtoDepDir != null) 419*cc02d7e2SAndroid Build Coastguard Worker { 420*cc02d7e2SAndroid Build Coastguard Worker DependencyOut = DepFileUtil.GetDepFilenameForProto(ProtoDepDir, Protobuf[0].ItemSpec); 421*cc02d7e2SAndroid Build Coastguard Worker } 422*cc02d7e2SAndroid Build Coastguard Worker 423*cc02d7e2SAndroid Build Coastguard Worker if (GrpcPluginExe == null) 424*cc02d7e2SAndroid Build Coastguard Worker { 425*cc02d7e2SAndroid Build Coastguard Worker GrpcOutputOptions = null; 426*cc02d7e2SAndroid Build Coastguard Worker GrpcOutputDir = null; 427*cc02d7e2SAndroid Build Coastguard Worker } 428*cc02d7e2SAndroid Build Coastguard Worker else if (GrpcOutputDir == null) 429*cc02d7e2SAndroid Build Coastguard Worker { 430*cc02d7e2SAndroid Build Coastguard Worker // Use OutputDir for gRPC output if not specified otherwise by user. 431*cc02d7e2SAndroid Build Coastguard Worker GrpcOutputDir = OutputDir; 432*cc02d7e2SAndroid Build Coastguard Worker } 433*cc02d7e2SAndroid Build Coastguard Worker 434*cc02d7e2SAndroid Build Coastguard Worker return !Log.HasLoggedErrors && base.ValidateParameters(); 435*cc02d7e2SAndroid Build Coastguard Worker } 436*cc02d7e2SAndroid Build Coastguard Worker 437*cc02d7e2SAndroid Build Coastguard Worker // Protoc chokes on BOM, naturally. I would! 438*cc02d7e2SAndroid Build Coastguard Worker static readonly Encoding s_utf8WithoutBom = new UTF8Encoding(false); 439*cc02d7e2SAndroid Build Coastguard Worker protected override Encoding ResponseFileEncoding => s_utf8WithoutBom; 440*cc02d7e2SAndroid Build Coastguard Worker 441*cc02d7e2SAndroid Build Coastguard Worker // Protoc takes one argument per line from the response file, and does not 442*cc02d7e2SAndroid Build Coastguard Worker // require any quoting whatsoever. Otherwise, this is similar to the 443*cc02d7e2SAndroid Build Coastguard Worker // standard CommandLineBuilder 444*cc02d7e2SAndroid Build Coastguard Worker class ProtocResponseFileBuilder 445*cc02d7e2SAndroid Build Coastguard Worker { 446*cc02d7e2SAndroid Build Coastguard Worker StringBuilder _data = new StringBuilder(1000); ToString()447*cc02d7e2SAndroid Build Coastguard Worker public override string ToString() => _data.ToString(); 448*cc02d7e2SAndroid Build Coastguard Worker 449*cc02d7e2SAndroid Build Coastguard Worker // If 'value' is not empty, append '--name=value\n'. AddSwitchMaybe(string name, string value)450*cc02d7e2SAndroid Build Coastguard Worker public void AddSwitchMaybe(string name, string value) 451*cc02d7e2SAndroid Build Coastguard Worker { 452*cc02d7e2SAndroid Build Coastguard Worker if (!string.IsNullOrEmpty(value)) 453*cc02d7e2SAndroid Build Coastguard Worker { 454*cc02d7e2SAndroid Build Coastguard Worker _data.Append("--").Append(name).Append("=") 455*cc02d7e2SAndroid Build Coastguard Worker .Append(value).Append('\n'); 456*cc02d7e2SAndroid Build Coastguard Worker } 457*cc02d7e2SAndroid Build Coastguard Worker } 458*cc02d7e2SAndroid Build Coastguard Worker 459*cc02d7e2SAndroid Build Coastguard Worker // Add switch with the 'values' separated by commas, for options. AddSwitchMaybe(string name, string[] values)460*cc02d7e2SAndroid Build Coastguard Worker public void AddSwitchMaybe(string name, string[] values) 461*cc02d7e2SAndroid Build Coastguard Worker { 462*cc02d7e2SAndroid Build Coastguard Worker if (values?.Length > 0) 463*cc02d7e2SAndroid Build Coastguard Worker { 464*cc02d7e2SAndroid Build Coastguard Worker _data.Append("--").Append(name).Append("=") 465*cc02d7e2SAndroid Build Coastguard Worker .Append(string.Join(",", values)).Append('\n'); 466*cc02d7e2SAndroid Build Coastguard Worker } 467*cc02d7e2SAndroid Build Coastguard Worker } 468*cc02d7e2SAndroid Build Coastguard Worker 469*cc02d7e2SAndroid Build Coastguard Worker // Add a positional argument to the file data. AddArg(string arg)470*cc02d7e2SAndroid Build Coastguard Worker public void AddArg(string arg) 471*cc02d7e2SAndroid Build Coastguard Worker { 472*cc02d7e2SAndroid Build Coastguard Worker _data.Append(arg).Append('\n'); 473*cc02d7e2SAndroid Build Coastguard Worker } 474*cc02d7e2SAndroid Build Coastguard Worker }; 475*cc02d7e2SAndroid Build Coastguard Worker 476*cc02d7e2SAndroid Build Coastguard Worker // Called by the base ToolTask to get response file contents. GenerateResponseFileCommands()477*cc02d7e2SAndroid Build Coastguard Worker protected override string GenerateResponseFileCommands() 478*cc02d7e2SAndroid Build Coastguard Worker { 479*cc02d7e2SAndroid Build Coastguard Worker var cmd = new ProtocResponseFileBuilder(); 480*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe(Generator + "_out", TrimEndSlash(OutputDir)); 481*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe(Generator + "_opt", OutputOptions); 482*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe("plugin=protoc-gen-grpc", GrpcPluginExe); 483*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe("grpc_out", TrimEndSlash(GrpcOutputDir)); 484*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe("grpc_opt", GrpcOutputOptions); 485*cc02d7e2SAndroid Build Coastguard Worker if (ProtoPath != null) 486*cc02d7e2SAndroid Build Coastguard Worker { 487*cc02d7e2SAndroid Build Coastguard Worker foreach (string path in ProtoPath) 488*cc02d7e2SAndroid Build Coastguard Worker { 489*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe("proto_path", TrimEndSlash(path)); 490*cc02d7e2SAndroid Build Coastguard Worker } 491*cc02d7e2SAndroid Build Coastguard Worker } 492*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe("dependency_out", DependencyOut); 493*cc02d7e2SAndroid Build Coastguard Worker cmd.AddSwitchMaybe("error_format", "msvs"); 494*cc02d7e2SAndroid Build Coastguard Worker 495*cc02d7e2SAndroid Build Coastguard Worker if (AdditionalProtocArguments != null) 496*cc02d7e2SAndroid Build Coastguard Worker { 497*cc02d7e2SAndroid Build Coastguard Worker foreach (var additionalProtocOption in AdditionalProtocArguments) 498*cc02d7e2SAndroid Build Coastguard Worker { 499*cc02d7e2SAndroid Build Coastguard Worker cmd.AddArg(additionalProtocOption); 500*cc02d7e2SAndroid Build Coastguard Worker } 501*cc02d7e2SAndroid Build Coastguard Worker } 502*cc02d7e2SAndroid Build Coastguard Worker 503*cc02d7e2SAndroid Build Coastguard Worker foreach (var proto in Protobuf) 504*cc02d7e2SAndroid Build Coastguard Worker { 505*cc02d7e2SAndroid Build Coastguard Worker cmd.AddArg(proto.ItemSpec); 506*cc02d7e2SAndroid Build Coastguard Worker } 507*cc02d7e2SAndroid Build Coastguard Worker return cmd.ToString(); 508*cc02d7e2SAndroid Build Coastguard Worker } 509*cc02d7e2SAndroid Build Coastguard Worker 510*cc02d7e2SAndroid Build Coastguard Worker // Protoc cannot digest trailing slashes in directory names, 511*cc02d7e2SAndroid Build Coastguard Worker // curiously under Linux, but not in Windows. TrimEndSlash(string dir)512*cc02d7e2SAndroid Build Coastguard Worker static string TrimEndSlash(string dir) 513*cc02d7e2SAndroid Build Coastguard Worker { 514*cc02d7e2SAndroid Build Coastguard Worker if (dir == null || dir.Length <= 1) 515*cc02d7e2SAndroid Build Coastguard Worker { 516*cc02d7e2SAndroid Build Coastguard Worker return dir; 517*cc02d7e2SAndroid Build Coastguard Worker } 518*cc02d7e2SAndroid Build Coastguard Worker string trim = dir.TrimEnd('/', '\\'); 519*cc02d7e2SAndroid Build Coastguard Worker // Do not trim the root slash, drive letter possible. 520*cc02d7e2SAndroid Build Coastguard Worker if (trim.Length == 0) 521*cc02d7e2SAndroid Build Coastguard Worker { 522*cc02d7e2SAndroid Build Coastguard Worker // Slashes all the way down. 523*cc02d7e2SAndroid Build Coastguard Worker return dir.Substring(0, 1); 524*cc02d7e2SAndroid Build Coastguard Worker } 525*cc02d7e2SAndroid Build Coastguard Worker if (trim.Length == 2 && dir.Length > 2 && trim[1] == ':') 526*cc02d7e2SAndroid Build Coastguard Worker { 527*cc02d7e2SAndroid Build Coastguard Worker // We have a drive letter and root, e. g. 'C:\' 528*cc02d7e2SAndroid Build Coastguard Worker return dir.Substring(0, 3); 529*cc02d7e2SAndroid Build Coastguard Worker } 530*cc02d7e2SAndroid Build Coastguard Worker return trim; 531*cc02d7e2SAndroid Build Coastguard Worker } 532*cc02d7e2SAndroid Build Coastguard Worker 533*cc02d7e2SAndroid Build Coastguard Worker // Called by the base class to log tool's command line. 534*cc02d7e2SAndroid Build Coastguard Worker // 535*cc02d7e2SAndroid Build Coastguard Worker // Protoc command file is peculiar, with one argument per line, separated 536*cc02d7e2SAndroid Build Coastguard Worker // by newlines. Unwrap it for log readability into a single line, and also 537*cc02d7e2SAndroid Build Coastguard Worker // quote arguments, lest it look weird and so it may be copied and pasted 538*cc02d7e2SAndroid Build Coastguard Worker // into shell. Since this is for logging only, correct enough is correct. LogToolCommand(string cmd)539*cc02d7e2SAndroid Build Coastguard Worker protected override void LogToolCommand(string cmd) 540*cc02d7e2SAndroid Build Coastguard Worker { 541*cc02d7e2SAndroid Build Coastguard Worker var printer = new StringBuilder(1024); 542*cc02d7e2SAndroid Build Coastguard Worker 543*cc02d7e2SAndroid Build Coastguard Worker // Print 'str' slice into 'printer', wrapping in quotes if contains some 544*cc02d7e2SAndroid Build Coastguard Worker // interesting characters in file names, or if empty string. The list of 545*cc02d7e2SAndroid Build Coastguard Worker // characters requiring quoting is not by any means exhaustive; we are 546*cc02d7e2SAndroid Build Coastguard Worker // just striving to be nice, not guaranteeing to be nice. 547*cc02d7e2SAndroid Build Coastguard Worker var quotable = new[] { ' ', '!', '$', '&', '\'', '^' }; 548*cc02d7e2SAndroid Build Coastguard Worker void PrintQuoting(string str, int start, int count) 549*cc02d7e2SAndroid Build Coastguard Worker { 550*cc02d7e2SAndroid Build Coastguard Worker bool wrap = count == 0 || str.IndexOfAny(quotable, start, count) >= 0; 551*cc02d7e2SAndroid Build Coastguard Worker if (wrap) printer.Append('"'); 552*cc02d7e2SAndroid Build Coastguard Worker printer.Append(str, start, count); 553*cc02d7e2SAndroid Build Coastguard Worker if (wrap) printer.Append('"'); 554*cc02d7e2SAndroid Build Coastguard Worker } 555*cc02d7e2SAndroid Build Coastguard Worker 556*cc02d7e2SAndroid Build Coastguard Worker for (int ib = 0, ie; (ie = cmd.IndexOf('\n', ib)) >= 0; ib = ie + 1) 557*cc02d7e2SAndroid Build Coastguard Worker { 558*cc02d7e2SAndroid Build Coastguard Worker // First line only contains both the program name and the first switch. 559*cc02d7e2SAndroid Build Coastguard Worker // We can rely on at least the '--out_dir' switch being always present. 560*cc02d7e2SAndroid Build Coastguard Worker if (ib == 0) 561*cc02d7e2SAndroid Build Coastguard Worker { 562*cc02d7e2SAndroid Build Coastguard Worker int iep = cmd.IndexOf(" --"); 563*cc02d7e2SAndroid Build Coastguard Worker if (iep > 0) 564*cc02d7e2SAndroid Build Coastguard Worker { 565*cc02d7e2SAndroid Build Coastguard Worker PrintQuoting(cmd, 0, iep); 566*cc02d7e2SAndroid Build Coastguard Worker ib = iep + 1; 567*cc02d7e2SAndroid Build Coastguard Worker } 568*cc02d7e2SAndroid Build Coastguard Worker } 569*cc02d7e2SAndroid Build Coastguard Worker printer.Append(' '); 570*cc02d7e2SAndroid Build Coastguard Worker if (cmd[ib] == '-') 571*cc02d7e2SAndroid Build Coastguard Worker { 572*cc02d7e2SAndroid Build Coastguard Worker // Print switch unquoted, including '=' if any. 573*cc02d7e2SAndroid Build Coastguard Worker int iarg = cmd.IndexOf('=', ib, ie - ib); 574*cc02d7e2SAndroid Build Coastguard Worker if (iarg < 0) 575*cc02d7e2SAndroid Build Coastguard Worker { 576*cc02d7e2SAndroid Build Coastguard Worker // Bare switch without a '='. 577*cc02d7e2SAndroid Build Coastguard Worker printer.Append(cmd, ib, ie - ib); 578*cc02d7e2SAndroid Build Coastguard Worker continue; 579*cc02d7e2SAndroid Build Coastguard Worker } 580*cc02d7e2SAndroid Build Coastguard Worker printer.Append(cmd, ib, iarg + 1 - ib); 581*cc02d7e2SAndroid Build Coastguard Worker ib = iarg + 1; 582*cc02d7e2SAndroid Build Coastguard Worker } 583*cc02d7e2SAndroid Build Coastguard Worker // A positional argument or switch value. 584*cc02d7e2SAndroid Build Coastguard Worker PrintQuoting(cmd, ib, ie - ib); 585*cc02d7e2SAndroid Build Coastguard Worker } 586*cc02d7e2SAndroid Build Coastguard Worker 587*cc02d7e2SAndroid Build Coastguard Worker base.LogToolCommand(printer.ToString()); 588*cc02d7e2SAndroid Build Coastguard Worker } 589*cc02d7e2SAndroid Build Coastguard Worker LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance)590*cc02d7e2SAndroid Build Coastguard Worker protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) 591*cc02d7e2SAndroid Build Coastguard Worker { 592*cc02d7e2SAndroid Build Coastguard Worker foreach (ErrorListFilter filter in s_errorListFilters) 593*cc02d7e2SAndroid Build Coastguard Worker { 594*cc02d7e2SAndroid Build Coastguard Worker try 595*cc02d7e2SAndroid Build Coastguard Worker { 596*cc02d7e2SAndroid Build Coastguard Worker Match match = filter.Pattern.Match(singleLine); 597*cc02d7e2SAndroid Build Coastguard Worker 598*cc02d7e2SAndroid Build Coastguard Worker if (match.Success) 599*cc02d7e2SAndroid Build Coastguard Worker { 600*cc02d7e2SAndroid Build Coastguard Worker filter.LogAction(Log, match); 601*cc02d7e2SAndroid Build Coastguard Worker return; 602*cc02d7e2SAndroid Build Coastguard Worker } 603*cc02d7e2SAndroid Build Coastguard Worker } catch (RegexMatchTimeoutException) 604*cc02d7e2SAndroid Build Coastguard Worker { 605*cc02d7e2SAndroid Build Coastguard Worker Log.LogWarning("Unable to parse output from protoc. Regex timeout."); 606*cc02d7e2SAndroid Build Coastguard Worker } 607*cc02d7e2SAndroid Build Coastguard Worker } 608*cc02d7e2SAndroid Build Coastguard Worker 609*cc02d7e2SAndroid Build Coastguard Worker base.LogEventsFromTextOutput(singleLine, messageImportance); 610*cc02d7e2SAndroid Build Coastguard Worker } 611*cc02d7e2SAndroid Build Coastguard Worker 612*cc02d7e2SAndroid Build Coastguard Worker // Main task entry point. Execute()613*cc02d7e2SAndroid Build Coastguard Worker public override bool Execute() 614*cc02d7e2SAndroid Build Coastguard Worker { 615*cc02d7e2SAndroid Build Coastguard Worker base.UseCommandProcessor = false; 616*cc02d7e2SAndroid Build Coastguard Worker 617*cc02d7e2SAndroid Build Coastguard Worker bool ok = base.Execute(); 618*cc02d7e2SAndroid Build Coastguard Worker if (!ok) 619*cc02d7e2SAndroid Build Coastguard Worker { 620*cc02d7e2SAndroid Build Coastguard Worker return false; 621*cc02d7e2SAndroid Build Coastguard Worker } 622*cc02d7e2SAndroid Build Coastguard Worker 623*cc02d7e2SAndroid Build Coastguard Worker // Read dependency output file from the compiler to retrieve the 624*cc02d7e2SAndroid Build Coastguard Worker // definitive list of created files. Report the dependency file 625*cc02d7e2SAndroid Build Coastguard Worker // itself as having been written to. 626*cc02d7e2SAndroid Build Coastguard Worker if (DependencyOut != null) 627*cc02d7e2SAndroid Build Coastguard Worker { 628*cc02d7e2SAndroid Build Coastguard Worker string[] outputs = DepFileUtil.ReadDependencyOutputs(DependencyOut, Log); 629*cc02d7e2SAndroid Build Coastguard Worker if (HasLoggedErrors) 630*cc02d7e2SAndroid Build Coastguard Worker { 631*cc02d7e2SAndroid Build Coastguard Worker return false; 632*cc02d7e2SAndroid Build Coastguard Worker } 633*cc02d7e2SAndroid Build Coastguard Worker 634*cc02d7e2SAndroid Build Coastguard Worker GeneratedFiles = new ITaskItem[outputs.Length]; 635*cc02d7e2SAndroid Build Coastguard Worker for (int i = 0; i < outputs.Length; i++) 636*cc02d7e2SAndroid Build Coastguard Worker { 637*cc02d7e2SAndroid Build Coastguard Worker GeneratedFiles[i] = new TaskItem(outputs[i]); 638*cc02d7e2SAndroid Build Coastguard Worker } 639*cc02d7e2SAndroid Build Coastguard Worker AdditionalFileWrites = new ITaskItem[] { new TaskItem(DependencyOut) }; 640*cc02d7e2SAndroid Build Coastguard Worker } 641*cc02d7e2SAndroid Build Coastguard Worker 642*cc02d7e2SAndroid Build Coastguard Worker return true; 643*cc02d7e2SAndroid Build Coastguard Worker } 644*cc02d7e2SAndroid Build Coastguard Worker 645*cc02d7e2SAndroid Build Coastguard Worker class ErrorListFilter 646*cc02d7e2SAndroid Build Coastguard Worker { 647*cc02d7e2SAndroid Build Coastguard Worker public Regex Pattern { get; set; } 648*cc02d7e2SAndroid Build Coastguard Worker public Action<TaskLoggingHelper, Match> LogAction { get; set; } 649*cc02d7e2SAndroid Build Coastguard Worker } 650*cc02d7e2SAndroid Build Coastguard Worker }; 651*cc02d7e2SAndroid Build Coastguard Worker } 652