1*cc02d7e2SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*cc02d7e2SAndroid Build Coastguard Worker# Copyright 2022 The gRPC Authors 3*cc02d7e2SAndroid Build Coastguard Worker# 4*cc02d7e2SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*cc02d7e2SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*cc02d7e2SAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*cc02d7e2SAndroid Build Coastguard Worker# 8*cc02d7e2SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*cc02d7e2SAndroid Build Coastguard Worker# 10*cc02d7e2SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*cc02d7e2SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*cc02d7e2SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*cc02d7e2SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*cc02d7e2SAndroid Build Coastguard Worker# limitations under the License. 15*cc02d7e2SAndroid Build Coastguard Worker 16*cc02d7e2SAndroid Build Coastguard Worker# Fake protobuf compiler for use in the Grpc.Tools MSBuild integration 17*cc02d7e2SAndroid Build Coastguard Worker# unit tests. Its purpose is to be called from the Grpc.Tools 18*cc02d7e2SAndroid Build Coastguard Worker# Google.Protobuf.Tools.targets MSBuild file instead of the actual protoc 19*cc02d7e2SAndroid Build Coastguard Worker# compiler. This script: 20*cc02d7e2SAndroid Build Coastguard Worker# - parses the command line arguments 21*cc02d7e2SAndroid Build Coastguard Worker# - generates expected dependencies file 22*cc02d7e2SAndroid Build Coastguard Worker# - generates dummy .cs files that are expected by the tests 23*cc02d7e2SAndroid Build Coastguard Worker# - writes a JSON results file containing the arguments passed in 24*cc02d7e2SAndroid Build Coastguard Worker 25*cc02d7e2SAndroid Build Coastguard Worker# Configuration is done via environment variables as it is not possible 26*cc02d7e2SAndroid Build Coastguard Worker# to pass additional argument when called from the MSBuild scripts under test. 27*cc02d7e2SAndroid Build Coastguard Worker# 28*cc02d7e2SAndroid Build Coastguard Worker# Environment variables: 29*cc02d7e2SAndroid Build Coastguard Worker# FAKEPROTOC_PROJECTDIR - project directory 30*cc02d7e2SAndroid Build Coastguard Worker# FAKEPROTOC_OUTDIR - output directory for generated files and output file 31*cc02d7e2SAndroid Build Coastguard Worker# FAKEPROTOC_GENERATE_EXPECTED - list of expected generated files in format: 32*cc02d7e2SAndroid Build Coastguard Worker# file1.proto:csfile1.cs;csfile2.cs|file2.proto:csfile3.cs;csfile4.cs|... 33*cc02d7e2SAndroid Build Coastguard Worker 34*cc02d7e2SAndroid Build Coastguard Workerimport datetime 35*cc02d7e2SAndroid Build Coastguard Workerimport hashlib 36*cc02d7e2SAndroid Build Coastguard Workerimport json 37*cc02d7e2SAndroid Build Coastguard Workerimport os 38*cc02d7e2SAndroid Build Coastguard Workerimport sys 39*cc02d7e2SAndroid Build Coastguard Worker 40*cc02d7e2SAndroid Build Coastguard Worker# Set to True to write out debug messages from this script 41*cc02d7e2SAndroid Build Coastguard Worker_dbg = True 42*cc02d7e2SAndroid Build Coastguard Worker# file to which write the debug log 43*cc02d7e2SAndroid Build Coastguard Worker_dbgfile = None 44*cc02d7e2SAndroid Build Coastguard Worker 45*cc02d7e2SAndroid Build Coastguard Worker 46*cc02d7e2SAndroid Build Coastguard Workerdef _open_debug_log(filename): 47*cc02d7e2SAndroid Build Coastguard Worker """Create debug file for this script.""" 48*cc02d7e2SAndroid Build Coastguard Worker global _dbgfile 49*cc02d7e2SAndroid Build Coastguard Worker if _dbg: 50*cc02d7e2SAndroid Build Coastguard Worker # append mode since this script may be called multiple times 51*cc02d7e2SAndroid Build Coastguard Worker # during one build/test 52*cc02d7e2SAndroid Build Coastguard Worker _dbgfile = open(filename, "a") 53*cc02d7e2SAndroid Build Coastguard Worker 54*cc02d7e2SAndroid Build Coastguard Worker 55*cc02d7e2SAndroid Build Coastguard Workerdef _close_debug_log(): 56*cc02d7e2SAndroid Build Coastguard Worker """Close the debug log file.""" 57*cc02d7e2SAndroid Build Coastguard Worker if _dbgfile: 58*cc02d7e2SAndroid Build Coastguard Worker _dbgfile.close() 59*cc02d7e2SAndroid Build Coastguard Worker 60*cc02d7e2SAndroid Build Coastguard Worker 61*cc02d7e2SAndroid Build Coastguard Workerdef _write_debug(msg): 62*cc02d7e2SAndroid Build Coastguard Worker """Write to the debug log file if debug is enabled.""" 63*cc02d7e2SAndroid Build Coastguard Worker if _dbg and _dbgfile: 64*cc02d7e2SAndroid Build Coastguard Worker print(msg, file=_dbgfile, flush=True) 65*cc02d7e2SAndroid Build Coastguard Worker 66*cc02d7e2SAndroid Build Coastguard Worker 67*cc02d7e2SAndroid Build Coastguard Workerdef _read_protoc_arguments(): 68*cc02d7e2SAndroid Build Coastguard Worker """ 69*cc02d7e2SAndroid Build Coastguard Worker Get the protoc argument from the command line and 70*cc02d7e2SAndroid Build Coastguard Worker any response files specified on the command line. 71*cc02d7e2SAndroid Build Coastguard Worker 72*cc02d7e2SAndroid Build Coastguard Worker Returns the list of arguments. 73*cc02d7e2SAndroid Build Coastguard Worker """ 74*cc02d7e2SAndroid Build Coastguard Worker _write_debug("\nread_protoc_arguments") 75*cc02d7e2SAndroid Build Coastguard Worker result = [] 76*cc02d7e2SAndroid Build Coastguard Worker for arg in sys.argv[1:]: 77*cc02d7e2SAndroid Build Coastguard Worker _write_debug(" arg: " + arg) 78*cc02d7e2SAndroid Build Coastguard Worker if arg.startswith("@"): 79*cc02d7e2SAndroid Build Coastguard Worker # TODO(jtattermusch): inserting a "commented out" argument feels hacky 80*cc02d7e2SAndroid Build Coastguard Worker result.append("# RSP file: %s" % arg) 81*cc02d7e2SAndroid Build Coastguard Worker rsp_file_name = arg[1:] 82*cc02d7e2SAndroid Build Coastguard Worker result.extend(_read_rsp_file(rsp_file_name)) 83*cc02d7e2SAndroid Build Coastguard Worker else: 84*cc02d7e2SAndroid Build Coastguard Worker result.append(arg) 85*cc02d7e2SAndroid Build Coastguard Worker return result 86*cc02d7e2SAndroid Build Coastguard Worker 87*cc02d7e2SAndroid Build Coastguard Worker 88*cc02d7e2SAndroid Build Coastguard Workerdef _read_rsp_file(rspfile): 89*cc02d7e2SAndroid Build Coastguard Worker """ 90*cc02d7e2SAndroid Build Coastguard Worker Returns list of arguments from a response file. 91*cc02d7e2SAndroid Build Coastguard Worker """ 92*cc02d7e2SAndroid Build Coastguard Worker _write_debug("\nread_rsp_file: " + rspfile) 93*cc02d7e2SAndroid Build Coastguard Worker result = [] 94*cc02d7e2SAndroid Build Coastguard Worker with open(rspfile, "r") as rsp: 95*cc02d7e2SAndroid Build Coastguard Worker for line in rsp: 96*cc02d7e2SAndroid Build Coastguard Worker line = line.strip() 97*cc02d7e2SAndroid Build Coastguard Worker _write_debug(" line: " + line) 98*cc02d7e2SAndroid Build Coastguard Worker result.append(line) 99*cc02d7e2SAndroid Build Coastguard Worker return result 100*cc02d7e2SAndroid Build Coastguard Worker 101*cc02d7e2SAndroid Build Coastguard Worker 102*cc02d7e2SAndroid Build Coastguard Workerdef _parse_protoc_arguments(protoc_args, projectdir): 103*cc02d7e2SAndroid Build Coastguard Worker """ 104*cc02d7e2SAndroid Build Coastguard Worker Parse the protoc arguments from the provided list 105*cc02d7e2SAndroid Build Coastguard Worker """ 106*cc02d7e2SAndroid Build Coastguard Worker 107*cc02d7e2SAndroid Build Coastguard Worker _write_debug("\nparse_protoc_arguments") 108*cc02d7e2SAndroid Build Coastguard Worker arg_dict = {} 109*cc02d7e2SAndroid Build Coastguard Worker for arg in protoc_args: 110*cc02d7e2SAndroid Build Coastguard Worker _write_debug("Parsing: %s" % arg) 111*cc02d7e2SAndroid Build Coastguard Worker 112*cc02d7e2SAndroid Build Coastguard Worker # All arguments containing file or directory paths are 113*cc02d7e2SAndroid Build Coastguard Worker # normalized by converting all '\' and changed to '/' 114*cc02d7e2SAndroid Build Coastguard Worker if arg.startswith("--"): 115*cc02d7e2SAndroid Build Coastguard Worker # Assumes that cmdline arguments are always passed in the 116*cc02d7e2SAndroid Build Coastguard Worker # "--somearg=argvalue", which happens to be the form that 117*cc02d7e2SAndroid Build Coastguard Worker # msbuild integration uses, but it's not the only way. 118*cc02d7e2SAndroid Build Coastguard Worker (name, value) = arg.split("=", 1) 119*cc02d7e2SAndroid Build Coastguard Worker 120*cc02d7e2SAndroid Build Coastguard Worker if ( 121*cc02d7e2SAndroid Build Coastguard Worker name == "--dependency_out" 122*cc02d7e2SAndroid Build Coastguard Worker or name == "--grpc_out" 123*cc02d7e2SAndroid Build Coastguard Worker or name == "--csharp_out" 124*cc02d7e2SAndroid Build Coastguard Worker ): 125*cc02d7e2SAndroid Build Coastguard Worker # For args that contain a path, make the path absolute and normalize it 126*cc02d7e2SAndroid Build Coastguard Worker # to make it easier to assert equality in tests. 127*cc02d7e2SAndroid Build Coastguard Worker value = _normalized_absolute_path(value) 128*cc02d7e2SAndroid Build Coastguard Worker 129*cc02d7e2SAndroid Build Coastguard Worker if name == "--proto_path": 130*cc02d7e2SAndroid Build Coastguard Worker # for simplicity keep this one as relative path rather than absolute path 131*cc02d7e2SAndroid Build Coastguard Worker # since it is an input file that is always be near the project file 132*cc02d7e2SAndroid Build Coastguard Worker value = _normalized_relative_to_projectdir(value, projectdir) 133*cc02d7e2SAndroid Build Coastguard Worker 134*cc02d7e2SAndroid Build Coastguard Worker _add_protoc_arg_to_dict(arg_dict, name, value) 135*cc02d7e2SAndroid Build Coastguard Worker 136*cc02d7e2SAndroid Build Coastguard Worker elif arg.startswith("#"): 137*cc02d7e2SAndroid Build Coastguard Worker pass # ignore 138*cc02d7e2SAndroid Build Coastguard Worker else: 139*cc02d7e2SAndroid Build Coastguard Worker # arg represents a proto file name 140*cc02d7e2SAndroid Build Coastguard Worker arg = _normalized_relative_to_projectdir(arg, projectdir) 141*cc02d7e2SAndroid Build Coastguard Worker _add_protoc_arg_to_dict(arg_dict, "protofile", arg) 142*cc02d7e2SAndroid Build Coastguard Worker return arg_dict 143*cc02d7e2SAndroid Build Coastguard Worker 144*cc02d7e2SAndroid Build Coastguard Worker 145*cc02d7e2SAndroid Build Coastguard Workerdef _add_protoc_arg_to_dict(arg_dict, name, value): 146*cc02d7e2SAndroid Build Coastguard Worker """ 147*cc02d7e2SAndroid Build Coastguard Worker Add the arguments with name/value to a multi-dictionary of arguments 148*cc02d7e2SAndroid Build Coastguard Worker """ 149*cc02d7e2SAndroid Build Coastguard Worker if name not in arg_dict: 150*cc02d7e2SAndroid Build Coastguard Worker arg_dict[name] = [] 151*cc02d7e2SAndroid Build Coastguard Worker 152*cc02d7e2SAndroid Build Coastguard Worker arg_dict[name].append(value) 153*cc02d7e2SAndroid Build Coastguard Worker 154*cc02d7e2SAndroid Build Coastguard Worker 155*cc02d7e2SAndroid Build Coastguard Workerdef _normalized_relative_to_projectdir(file, projectdir): 156*cc02d7e2SAndroid Build Coastguard Worker """Convert a file path to one relative to the project directory.""" 157*cc02d7e2SAndroid Build Coastguard Worker try: 158*cc02d7e2SAndroid Build Coastguard Worker return _normalize_slashes( 159*cc02d7e2SAndroid Build Coastguard Worker os.path.relpath(os.path.abspath(file), projectdir) 160*cc02d7e2SAndroid Build Coastguard Worker ) 161*cc02d7e2SAndroid Build Coastguard Worker except ValueError: 162*cc02d7e2SAndroid Build Coastguard Worker # On Windows if the paths are on different drives then we get this error 163*cc02d7e2SAndroid Build Coastguard Worker # Just return the absolute path 164*cc02d7e2SAndroid Build Coastguard Worker return _normalize_slashes(os.path.abspath(file)) 165*cc02d7e2SAndroid Build Coastguard Worker 166*cc02d7e2SAndroid Build Coastguard Worker 167*cc02d7e2SAndroid Build Coastguard Workerdef _normalized_absolute_path(file): 168*cc02d7e2SAndroid Build Coastguard Worker """Returns normalized absolute path to file.""" 169*cc02d7e2SAndroid Build Coastguard Worker return _normalize_slashes(os.path.abspath(file)) 170*cc02d7e2SAndroid Build Coastguard Worker 171*cc02d7e2SAndroid Build Coastguard Worker 172*cc02d7e2SAndroid Build Coastguard Workerdef _normalize_slashes(path): 173*cc02d7e2SAndroid Build Coastguard Worker """Change all backslashes to forward slashes to make comparing path strings easier.""" 174*cc02d7e2SAndroid Build Coastguard Worker return path.replace("\\", "/") 175*cc02d7e2SAndroid Build Coastguard Worker 176*cc02d7e2SAndroid Build Coastguard Worker 177*cc02d7e2SAndroid Build Coastguard Workerdef _write_or_update_results_json(log_dir, protofile, protoc_arg_dict): 178*cc02d7e2SAndroid Build Coastguard Worker """Write or update the results JSON file""" 179*cc02d7e2SAndroid Build Coastguard Worker 180*cc02d7e2SAndroid Build Coastguard Worker # Read existing json. 181*cc02d7e2SAndroid Build Coastguard Worker # Since protoc may be called more than once each build/test if there is 182*cc02d7e2SAndroid Build Coastguard Worker # more than one protoc file, we read the existing data to add to it. 183*cc02d7e2SAndroid Build Coastguard Worker fname = os.path.abspath("%s/results.json" % log_dir) 184*cc02d7e2SAndroid Build Coastguard Worker if os.path.isfile(fname): 185*cc02d7e2SAndroid Build Coastguard Worker # Load the original contents. 186*cc02d7e2SAndroid Build Coastguard Worker with open(fname, "r") as forig: 187*cc02d7e2SAndroid Build Coastguard Worker results_json = json.load(forig) 188*cc02d7e2SAndroid Build Coastguard Worker else: 189*cc02d7e2SAndroid Build Coastguard Worker results_json = {} 190*cc02d7e2SAndroid Build Coastguard Worker results_json["Files"] = {} 191*cc02d7e2SAndroid Build Coastguard Worker 192*cc02d7e2SAndroid Build Coastguard Worker results_json["Files"][protofile] = protoc_arg_dict 193*cc02d7e2SAndroid Build Coastguard Worker results_json["Metadata"] = {"timestamp": str(datetime.datetime.now())} 194*cc02d7e2SAndroid Build Coastguard Worker 195*cc02d7e2SAndroid Build Coastguard Worker with open(fname, "w") as fout: 196*cc02d7e2SAndroid Build Coastguard Worker json.dump(results_json, fout, indent=4) 197*cc02d7e2SAndroid Build Coastguard Worker 198*cc02d7e2SAndroid Build Coastguard Worker 199*cc02d7e2SAndroid Build Coastguard Workerdef _parse_generate_expected(generate_expected_str): 200*cc02d7e2SAndroid Build Coastguard Worker """ 201*cc02d7e2SAndroid Build Coastguard Worker Parse FAKEPROTOC_GENERATE_EXPECTED that specifies the proto files 202*cc02d7e2SAndroid Build Coastguard Worker and the cs files to generate. We rely on the test to say what is 203*cc02d7e2SAndroid Build Coastguard Worker expected rather than trying to work it out in this script. 204*cc02d7e2SAndroid Build Coastguard Worker 205*cc02d7e2SAndroid Build Coastguard Worker The format of the input is: 206*cc02d7e2SAndroid Build Coastguard Worker file1.proto:csfile1.cs;csfile2.cs|file2.proto:csfile3.cs;csfile4.cs|... 207*cc02d7e2SAndroid Build Coastguard Worker """ 208*cc02d7e2SAndroid Build Coastguard Worker _write_debug("\nparse_generate_expected") 209*cc02d7e2SAndroid Build Coastguard Worker 210*cc02d7e2SAndroid Build Coastguard Worker result = {} 211*cc02d7e2SAndroid Build Coastguard Worker entries = generate_expected_str.split("|") 212*cc02d7e2SAndroid Build Coastguard Worker for entry in entries: 213*cc02d7e2SAndroid Build Coastguard Worker parts = entry.split(":") 214*cc02d7e2SAndroid Build Coastguard Worker pfile = _normalize_slashes(parts[0]) 215*cc02d7e2SAndroid Build Coastguard Worker csfiles = parts[1].split(";") 216*cc02d7e2SAndroid Build Coastguard Worker result[pfile] = csfiles 217*cc02d7e2SAndroid Build Coastguard Worker _write_debug(pfile + " : " + str(csfiles)) 218*cc02d7e2SAndroid Build Coastguard Worker return result 219*cc02d7e2SAndroid Build Coastguard Worker 220*cc02d7e2SAndroid Build Coastguard Worker 221*cc02d7e2SAndroid Build Coastguard Workerdef _get_cs_files_to_generate(protofile, proto_to_generated): 222*cc02d7e2SAndroid Build Coastguard Worker """Returns list of .cs files to generated based on FAKEPROTOC_GENERATE_EXPECTED env.""" 223*cc02d7e2SAndroid Build Coastguard Worker protoname_normalized = _normalize_slashes(protofile) 224*cc02d7e2SAndroid Build Coastguard Worker cs_files_to_generate = proto_to_generated.get(protoname_normalized) 225*cc02d7e2SAndroid Build Coastguard Worker return cs_files_to_generate 226*cc02d7e2SAndroid Build Coastguard Worker 227*cc02d7e2SAndroid Build Coastguard Worker 228*cc02d7e2SAndroid Build Coastguard Workerdef _is_grpc_out_file(csfile): 229*cc02d7e2SAndroid Build Coastguard Worker """Return true if the file is one that would be generated by gRPC plugin""" 230*cc02d7e2SAndroid Build Coastguard Worker # This is using the heuristics of checking that the name of the file 231*cc02d7e2SAndroid Build Coastguard Worker # matches *Grpc.cs which is the name that the gRPC plugin would produce. 232*cc02d7e2SAndroid Build Coastguard Worker return csfile.endswith("Grpc.cs") 233*cc02d7e2SAndroid Build Coastguard Worker 234*cc02d7e2SAndroid Build Coastguard Worker 235*cc02d7e2SAndroid Build Coastguard Workerdef _generate_cs_files( 236*cc02d7e2SAndroid Build Coastguard Worker protofile, cs_files_to_generate, grpc_out_dir, csharp_out_dir, projectdir 237*cc02d7e2SAndroid Build Coastguard Worker): 238*cc02d7e2SAndroid Build Coastguard Worker """Create expected cs files.""" 239*cc02d7e2SAndroid Build Coastguard Worker _write_debug("\ngenerate_cs_files") 240*cc02d7e2SAndroid Build Coastguard Worker 241*cc02d7e2SAndroid Build Coastguard Worker if not cs_files_to_generate: 242*cc02d7e2SAndroid Build Coastguard Worker _write_debug("No .cs files matching proto file name %s" % protofile) 243*cc02d7e2SAndroid Build Coastguard Worker return 244*cc02d7e2SAndroid Build Coastguard Worker 245*cc02d7e2SAndroid Build Coastguard Worker if not os.path.isabs(grpc_out_dir): 246*cc02d7e2SAndroid Build Coastguard Worker # if not absolute, it is relative to project directory 247*cc02d7e2SAndroid Build Coastguard Worker grpc_out_dir = os.path.abspath("%s/%s" % (projectdir, grpc_out_dir)) 248*cc02d7e2SAndroid Build Coastguard Worker 249*cc02d7e2SAndroid Build Coastguard Worker if not os.path.isabs(csharp_out_dir): 250*cc02d7e2SAndroid Build Coastguard Worker # if not absolute, it is relative to project directory 251*cc02d7e2SAndroid Build Coastguard Worker csharp_out_dir = os.path.abspath("%s/%s" % (projectdir, csharp_out_dir)) 252*cc02d7e2SAndroid Build Coastguard Worker 253*cc02d7e2SAndroid Build Coastguard Worker # Ensure directories exist 254*cc02d7e2SAndroid Build Coastguard Worker if not os.path.isdir(grpc_out_dir): 255*cc02d7e2SAndroid Build Coastguard Worker os.makedirs(grpc_out_dir) 256*cc02d7e2SAndroid Build Coastguard Worker 257*cc02d7e2SAndroid Build Coastguard Worker if not os.path.isdir(csharp_out_dir): 258*cc02d7e2SAndroid Build Coastguard Worker os.makedirs(csharp_out_dir) 259*cc02d7e2SAndroid Build Coastguard Worker 260*cc02d7e2SAndroid Build Coastguard Worker timestamp = str(datetime.datetime.now()) 261*cc02d7e2SAndroid Build Coastguard Worker for csfile in cs_files_to_generate: 262*cc02d7e2SAndroid Build Coastguard Worker if csfile.endswith("Grpc.cs"): 263*cc02d7e2SAndroid Build Coastguard Worker csfile_fullpath = "%s/%s" % (grpc_out_dir, csfile) 264*cc02d7e2SAndroid Build Coastguard Worker else: 265*cc02d7e2SAndroid Build Coastguard Worker csfile_fullpath = "%s/%s" % (csharp_out_dir, csfile) 266*cc02d7e2SAndroid Build Coastguard Worker _write_debug("Creating: %s" % csfile_fullpath) 267*cc02d7e2SAndroid Build Coastguard Worker with open(csfile_fullpath, "w") as fout: 268*cc02d7e2SAndroid Build Coastguard Worker print("// Generated by fake protoc: %s" % timestamp, file=fout) 269*cc02d7e2SAndroid Build Coastguard Worker 270*cc02d7e2SAndroid Build Coastguard Worker 271*cc02d7e2SAndroid Build Coastguard Workerdef _create_dependency_file( 272*cc02d7e2SAndroid Build Coastguard Worker protofile, 273*cc02d7e2SAndroid Build Coastguard Worker cs_files_to_generate, 274*cc02d7e2SAndroid Build Coastguard Worker dependencyfile, 275*cc02d7e2SAndroid Build Coastguard Worker grpc_out_dir, 276*cc02d7e2SAndroid Build Coastguard Worker csharp_out_dir, 277*cc02d7e2SAndroid Build Coastguard Worker): 278*cc02d7e2SAndroid Build Coastguard Worker """Create the expected dependency file.""" 279*cc02d7e2SAndroid Build Coastguard Worker _write_debug("\ncreate_dependency_file") 280*cc02d7e2SAndroid Build Coastguard Worker 281*cc02d7e2SAndroid Build Coastguard Worker if not dependencyfile: 282*cc02d7e2SAndroid Build Coastguard Worker _write_debug("dependencyfile is not set.") 283*cc02d7e2SAndroid Build Coastguard Worker return 284*cc02d7e2SAndroid Build Coastguard Worker 285*cc02d7e2SAndroid Build Coastguard Worker if not cs_files_to_generate: 286*cc02d7e2SAndroid Build Coastguard Worker _write_debug("No .cs files matching proto file name %s" % protofile) 287*cc02d7e2SAndroid Build Coastguard Worker return 288*cc02d7e2SAndroid Build Coastguard Worker 289*cc02d7e2SAndroid Build Coastguard Worker _write_debug("Creating dependency file: %s" % dependencyfile) 290*cc02d7e2SAndroid Build Coastguard Worker with open(dependencyfile, "w") as out: 291*cc02d7e2SAndroid Build Coastguard Worker nfiles = len(cs_files_to_generate) 292*cc02d7e2SAndroid Build Coastguard Worker for i in range(0, nfiles): 293*cc02d7e2SAndroid Build Coastguard Worker csfile = cs_files_to_generate[i] 294*cc02d7e2SAndroid Build Coastguard Worker if csfile.endswith("Grpc.cs"): 295*cc02d7e2SAndroid Build Coastguard Worker cs_filename = os.path.join(grpc_out_dir, csfile) 296*cc02d7e2SAndroid Build Coastguard Worker else: 297*cc02d7e2SAndroid Build Coastguard Worker cs_filename = os.path.join(csharp_out_dir, csfile) 298*cc02d7e2SAndroid Build Coastguard Worker if i == nfiles - 1: 299*cc02d7e2SAndroid Build Coastguard Worker print("%s: %s" % (cs_filename, protofile), file=out) 300*cc02d7e2SAndroid Build Coastguard Worker else: 301*cc02d7e2SAndroid Build Coastguard Worker print("%s \\" % cs_filename, file=out) 302*cc02d7e2SAndroid Build Coastguard Worker 303*cc02d7e2SAndroid Build Coastguard Worker 304*cc02d7e2SAndroid Build Coastguard Workerdef _getenv(name): 305*cc02d7e2SAndroid Build Coastguard Worker # Note there is a bug in .NET core 3.x that lowercases the environment 306*cc02d7e2SAndroid Build Coastguard Worker # variable names when they are added via Process.StartInfo, so we need to 307*cc02d7e2SAndroid Build Coastguard Worker # check both cases here (only an issue on Linux which is case sensitive) 308*cc02d7e2SAndroid Build Coastguard Worker value = os.getenv(name) 309*cc02d7e2SAndroid Build Coastguard Worker if value is None: 310*cc02d7e2SAndroid Build Coastguard Worker value = os.getenv(name.lower()) 311*cc02d7e2SAndroid Build Coastguard Worker return value 312*cc02d7e2SAndroid Build Coastguard Worker 313*cc02d7e2SAndroid Build Coastguard Worker 314*cc02d7e2SAndroid Build Coastguard Workerdef _get_argument_last_occurrence_or_none(protoc_arg_dict, name): 315*cc02d7e2SAndroid Build Coastguard Worker # If argument was passed multiple times, take the last occurrence. 316*cc02d7e2SAndroid Build Coastguard Worker # If the value does not exist then return None 317*cc02d7e2SAndroid Build Coastguard Worker values = protoc_arg_dict.get(name) 318*cc02d7e2SAndroid Build Coastguard Worker if values is not None: 319*cc02d7e2SAndroid Build Coastguard Worker return values[-1] 320*cc02d7e2SAndroid Build Coastguard Worker return None 321*cc02d7e2SAndroid Build Coastguard Worker 322*cc02d7e2SAndroid Build Coastguard Worker 323*cc02d7e2SAndroid Build Coastguard Workerdef main(): 324*cc02d7e2SAndroid Build Coastguard Worker # Check environment variables for the additional arguments used in the tests. 325*cc02d7e2SAndroid Build Coastguard Worker 326*cc02d7e2SAndroid Build Coastguard Worker projectdir = _getenv("FAKEPROTOC_PROJECTDIR") 327*cc02d7e2SAndroid Build Coastguard Worker if not projectdir: 328*cc02d7e2SAndroid Build Coastguard Worker print("FAKEPROTOC_PROJECTDIR not set") 329*cc02d7e2SAndroid Build Coastguard Worker sys.exit(1) 330*cc02d7e2SAndroid Build Coastguard Worker projectdir = os.path.abspath(projectdir) 331*cc02d7e2SAndroid Build Coastguard Worker 332*cc02d7e2SAndroid Build Coastguard Worker # Output directory for generated files and output file 333*cc02d7e2SAndroid Build Coastguard Worker protoc_outdir = _getenv("FAKEPROTOC_OUTDIR") 334*cc02d7e2SAndroid Build Coastguard Worker if not protoc_outdir: 335*cc02d7e2SAndroid Build Coastguard Worker print("FAKEPROTOC_OUTDIR not set") 336*cc02d7e2SAndroid Build Coastguard Worker sys.exit(1) 337*cc02d7e2SAndroid Build Coastguard Worker protoc_outdir = os.path.abspath(protoc_outdir) 338*cc02d7e2SAndroid Build Coastguard Worker 339*cc02d7e2SAndroid Build Coastguard Worker # Get list of expected generated files from env variable 340*cc02d7e2SAndroid Build Coastguard Worker generate_expected = _getenv("FAKEPROTOC_GENERATE_EXPECTED") 341*cc02d7e2SAndroid Build Coastguard Worker if not generate_expected: 342*cc02d7e2SAndroid Build Coastguard Worker print("FAKEPROTOC_GENERATE_EXPECTED not set") 343*cc02d7e2SAndroid Build Coastguard Worker sys.exit(1) 344*cc02d7e2SAndroid Build Coastguard Worker 345*cc02d7e2SAndroid Build Coastguard Worker # Prepare the debug log 346*cc02d7e2SAndroid Build Coastguard Worker log_dir = os.path.join(protoc_outdir, "log") 347*cc02d7e2SAndroid Build Coastguard Worker if not os.path.isdir(log_dir): 348*cc02d7e2SAndroid Build Coastguard Worker os.makedirs(log_dir) 349*cc02d7e2SAndroid Build Coastguard Worker _open_debug_log("%s/fakeprotoc_log.txt" % log_dir) 350*cc02d7e2SAndroid Build Coastguard Worker 351*cc02d7e2SAndroid Build Coastguard Worker _write_debug( 352*cc02d7e2SAndroid Build Coastguard Worker ( 353*cc02d7e2SAndroid Build Coastguard Worker "##### fakeprotoc called at %s\n" 354*cc02d7e2SAndroid Build Coastguard Worker + "FAKEPROTOC_PROJECTDIR = %s\n" 355*cc02d7e2SAndroid Build Coastguard Worker + "FAKEPROTOC_GENERATE_EXPECTED = %s\n" 356*cc02d7e2SAndroid Build Coastguard Worker ) 357*cc02d7e2SAndroid Build Coastguard Worker % (datetime.datetime.now(), projectdir, generate_expected) 358*cc02d7e2SAndroid Build Coastguard Worker ) 359*cc02d7e2SAndroid Build Coastguard Worker 360*cc02d7e2SAndroid Build Coastguard Worker proto_to_generated = _parse_generate_expected(generate_expected) 361*cc02d7e2SAndroid Build Coastguard Worker protoc_args = _read_protoc_arguments() 362*cc02d7e2SAndroid Build Coastguard Worker protoc_arg_dict = _parse_protoc_arguments(protoc_args, projectdir) 363*cc02d7e2SAndroid Build Coastguard Worker 364*cc02d7e2SAndroid Build Coastguard Worker # If argument was passed multiple times, take the last occurrence of it. 365*cc02d7e2SAndroid Build Coastguard Worker # TODO(jtattermusch): handle multiple occurrences of the same argument 366*cc02d7e2SAndroid Build Coastguard Worker dependencyfile = _get_argument_last_occurrence_or_none( 367*cc02d7e2SAndroid Build Coastguard Worker protoc_arg_dict, "--dependency_out" 368*cc02d7e2SAndroid Build Coastguard Worker ) 369*cc02d7e2SAndroid Build Coastguard Worker grpcout = _get_argument_last_occurrence_or_none( 370*cc02d7e2SAndroid Build Coastguard Worker protoc_arg_dict, "--grpc_out" 371*cc02d7e2SAndroid Build Coastguard Worker ) 372*cc02d7e2SAndroid Build Coastguard Worker csharpout = _get_argument_last_occurrence_or_none( 373*cc02d7e2SAndroid Build Coastguard Worker protoc_arg_dict, "--csharp_out" 374*cc02d7e2SAndroid Build Coastguard Worker ) 375*cc02d7e2SAndroid Build Coastguard Worker 376*cc02d7e2SAndroid Build Coastguard Worker # --grpc_out might not be set in which case use --csharp_out 377*cc02d7e2SAndroid Build Coastguard Worker if grpcout is None: 378*cc02d7e2SAndroid Build Coastguard Worker grpcout = csharpout 379*cc02d7e2SAndroid Build Coastguard Worker 380*cc02d7e2SAndroid Build Coastguard Worker if len(protoc_arg_dict.get("protofile")) != 1: 381*cc02d7e2SAndroid Build Coastguard Worker # regular protoc can process multiple .proto files passed at once, but we know 382*cc02d7e2SAndroid Build Coastguard Worker # the Grpc.Tools msbuild integration only ever passes one .proto file per invocation. 383*cc02d7e2SAndroid Build Coastguard Worker print( 384*cc02d7e2SAndroid Build Coastguard Worker "Expecting to get exactly one .proto file argument per fakeprotoc" 385*cc02d7e2SAndroid Build Coastguard Worker " invocation." 386*cc02d7e2SAndroid Build Coastguard Worker ) 387*cc02d7e2SAndroid Build Coastguard Worker sys.exit(1) 388*cc02d7e2SAndroid Build Coastguard Worker protofile = protoc_arg_dict.get("protofile")[0] 389*cc02d7e2SAndroid Build Coastguard Worker 390*cc02d7e2SAndroid Build Coastguard Worker cs_files_to_generate = _get_cs_files_to_generate( 391*cc02d7e2SAndroid Build Coastguard Worker protofile=protofile, proto_to_generated=proto_to_generated 392*cc02d7e2SAndroid Build Coastguard Worker ) 393*cc02d7e2SAndroid Build Coastguard Worker 394*cc02d7e2SAndroid Build Coastguard Worker _create_dependency_file( 395*cc02d7e2SAndroid Build Coastguard Worker protofile=protofile, 396*cc02d7e2SAndroid Build Coastguard Worker cs_files_to_generate=cs_files_to_generate, 397*cc02d7e2SAndroid Build Coastguard Worker dependencyfile=dependencyfile, 398*cc02d7e2SAndroid Build Coastguard Worker grpc_out_dir=grpcout, 399*cc02d7e2SAndroid Build Coastguard Worker csharp_out_dir=csharpout, 400*cc02d7e2SAndroid Build Coastguard Worker ) 401*cc02d7e2SAndroid Build Coastguard Worker 402*cc02d7e2SAndroid Build Coastguard Worker _generate_cs_files( 403*cc02d7e2SAndroid Build Coastguard Worker protofile=protofile, 404*cc02d7e2SAndroid Build Coastguard Worker cs_files_to_generate=cs_files_to_generate, 405*cc02d7e2SAndroid Build Coastguard Worker grpc_out_dir=grpcout, 406*cc02d7e2SAndroid Build Coastguard Worker csharp_out_dir=csharpout, 407*cc02d7e2SAndroid Build Coastguard Worker projectdir=projectdir, 408*cc02d7e2SAndroid Build Coastguard Worker ) 409*cc02d7e2SAndroid Build Coastguard Worker 410*cc02d7e2SAndroid Build Coastguard Worker _write_or_update_results_json( 411*cc02d7e2SAndroid Build Coastguard Worker log_dir=log_dir, protofile=protofile, protoc_arg_dict=protoc_arg_dict 412*cc02d7e2SAndroid Build Coastguard Worker ) 413*cc02d7e2SAndroid Build Coastguard Worker 414*cc02d7e2SAndroid Build Coastguard Worker _close_debug_log() 415*cc02d7e2SAndroid Build Coastguard Worker 416*cc02d7e2SAndroid Build Coastguard Worker 417*cc02d7e2SAndroid Build Coastguard Workerif __name__ == "__main__": 418*cc02d7e2SAndroid Build Coastguard Worker main() 419