1 # Copyright (C) 2016 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 # use this file except in compliance with the License. You may obtain a copy of 5 # the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 # License for the specific language governing permissions and limitations under 13 # the License. 14 import logging 15 import os 16 import subprocess 17 import sys 18 from distutils.spawn import find_executable 19 from google import protobuf 20 from importlib import import_module 21 22 23 def compile_proto(proto_path, output_dir): 24 """Invoke Protocol Compiler to generate python from given source .proto.""" 25 # Find compiler path 26 protoc = None 27 if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']): 28 protoc = os.environ['PROTOC'] 29 if not protoc: 30 protoc = find_executable('protoc') 31 if not protoc: 32 logging.error( 33 "Cannot find protobuf compiler (>=3.0.0), please install" 34 "protobuf-compiler package. Prefer copying from <top>/prebuilts/tools" 35 ) 36 logging.error(" prebuilts/tools/linux-x86_64/protoc/bin/protoc") 37 logging.error("If prebuilts are not available, use apt-get:") 38 logging.error(" sudo apt-get install protobuf-compiler") 39 return None 40 # Validate input proto path 41 if not os.path.exists(proto_path): 42 logging.error('Can\'t find required file: %s\n' % proto_path) 43 return None 44 # Validate output py-proto path 45 if not os.path.exists(output_dir): 46 os.mkdirs(output_dir) 47 elif not os.path.isdir(output_dir): 48 logging.error("Output path is not a valid directory: %s" % 49 (output_dir)) 50 return None 51 input_dir = os.path.dirname(proto_path) 52 output_filename = os.path.basename(proto_path).replace('.proto', '_pb2.py') 53 output_path = os.path.join(output_dir, output_filename) 54 # Compiling proto 55 logging.info('Generating %s' % output_path) 56 protoc_command = [ 57 protoc, '-I=%s' % (input_dir), '--python_out=%s' % (output_dir), 58 proto_path 59 ] 60 if subprocess.call(protoc_command, stderr=subprocess.STDOUT) != 0: 61 logging.error("Fail to compile proto") 62 return None 63 output_module_name = os.path.splitext(output_filename)[0] 64 return output_module_name 65 66 67 def compile_import_proto(output_dir, proto_path): 68 """ 69 Compile protobuf from PROTO_PATH and put the result in OUTPUT_DIR. 70 Return the imported module to caller. 71 :param output_dir: To store generated python proto library 72 :param proto_path: Path to the .proto file that needs to be compiled 73 :return: python proto module 74 """ 75 output_module_name = compile_proto(proto_path, output_dir) 76 if not output_module_name: 77 return None 78 sys.path.append(output_dir) 79 output_module = None 80 try: 81 output_module = import_module(output_module_name) 82 except ImportError: 83 logging.error("Cannot import generated py-proto %s" % 84 (output_module_name)) 85 return output_module 86 87 88 def parse_proto_to_ascii(binary_proto_msg): 89 """ 90 Parse binary protobuf message to human readable ascii string 91 :param binary_proto_msg: 92 :return: ascii string of the message 93 """ 94 return protobuf.text_format.MessageToString(binary_proto_msg) 95