diff options
Diffstat (limited to 'third_party/waf/waflib/extras/protoc.py')
-rw-r--r-- | third_party/waf/waflib/extras/protoc.py | 183 |
1 files changed, 165 insertions, 18 deletions
diff --git a/third_party/waf/waflib/extras/protoc.py b/third_party/waf/waflib/extras/protoc.py index 97d1ef3c340..cb16e858182 100644 --- a/third_party/waf/waflib/extras/protoc.py +++ b/third_party/waf/waflib/extras/protoc.py @@ -1,16 +1,21 @@ +#! /usr/bin/env python +# encoding: utf-8 +# WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file + #!/usr/bin/env python # encoding: utf-8 # Philipp Bender, 2012 # Matt Clarkson, 2012 -import re +import re, os from waflib.Task import Task from waflib.TaskGen import extension +from waflib import Errors, Context """ A simple tool to integrate protocol buffers into your build system. -Example:: +Example for C++: def configure(conf): conf.load('compiler_cxx cxx protoc') @@ -19,9 +24,55 @@ Example:: bld( features = 'cxx cxxprogram' source = 'main.cpp file1.proto proto/file2.proto', - include = '. proto', + includes = '. proto', target = 'executable') +Example for Python: + + def configure(conf): + conf.load('python protoc') + + def build(bld): + bld( + features = 'py' + source = 'main.py file1.proto proto/file2.proto', + protoc_includes = 'proto') + +Example for both Python and C++ at same time: + + def configure(conf): + conf.load('cxx python protoc') + + def build(bld): + bld( + features = 'cxx py' + source = 'file1.proto proto/file2.proto', + protoc_includes = 'proto') # or includes + + +Example for Java: + + def options(opt): + opt.load('java') + + def configure(conf): + conf.load('python java protoc') + # Here you have to point to your protobuf-java JAR and have it in classpath + conf.env.CLASSPATH_PROTOBUF = ['protobuf-java-2.5.0.jar'] + + def build(bld): + bld( + features = 'javac protoc', + name = 'pbjava', + srcdir = 'inc/ src', # directories used by javac + source = ['inc/message_inc.proto', 'inc/message.proto'], + # source is used by protoc for .proto files + use = 'PROTOBUF', + protoc_includes = ['inc']) # for protoc to search dependencies + + + + Notes when using this tool: - protoc command line parsing is tricky. @@ -35,10 +86,9 @@ Notes when using this tool: """ class protoc(Task): - # protoc expects the input proto file to be an absolute path. - run_str = '${PROTOC} ${PROTOC_FLAGS} ${PROTOC_ST:INCPATHS} ${SRC[0].abspath()}' + run_str = '${PROTOC} ${PROTOC_FL:PROTOC_FLAGS} ${PROTOC_ST:INCPATHS} ${PROTOC_ST:PROTOC_INCPATHS} ${SRC[0].bldpath()}' color = 'BLUE' - ext_out = ['.h', 'pb.cc'] + ext_out = ['.h', 'pb.cc', '.py', '.java'] def scan(self): """ Scan .proto dependencies @@ -48,8 +98,17 @@ class protoc(Task): nodes = [] names = [] seen = [] + search_nodes = [] + + if not node: + return (nodes, names) - if not node: return (nodes, names) + if 'cxx' in self.generator.features: + search_nodes = self.generator.includes_nodes + + if 'py' in self.generator.features or 'javac' in self.generator.features: + for incpath in getattr(self.generator, 'protoc_includes', []): + search_nodes.append(self.generator.bld.path.find_node(incpath)) def parse_node(node): if node in seen: @@ -60,8 +119,8 @@ class protoc(Task): m = re.search(r'^import\s+"(.*)";.*(//)?.*', line) if m: dep = m.groups()[0] - for incpath in self.env.INCPATHS: - found = incpath.find_resource(dep) + for incnode in search_nodes: + found = incnode.find_resource(dep) if found: nodes.append(found) parse_node(found) @@ -73,20 +132,108 @@ class protoc(Task): @extension('.proto') def process_protoc(self, node): - cpp_node = node.change_ext('.pb.cc') - hpp_node = node.change_ext('.pb.h') - self.create_task('protoc', node, [cpp_node, hpp_node]) - self.source.append(cpp_node) - - if 'cxx' in self.features and not self.env.PROTOC_FLAGS: - #self.env.PROTOC_FLAGS = '--cpp_out=%s' % node.parent.get_bld().abspath() # <- this does not work - self.env.PROTOC_FLAGS = '--cpp_out=%s' % node.parent.get_bld().bldpath() + incdirs = [] + out_nodes = [] + protoc_flags = [] + + # ensure PROTOC_FLAGS is a list; a copy is used below anyway + self.env.PROTOC_FLAGS = self.to_list(self.env.PROTOC_FLAGS) + + if 'cxx' in self.features: + cpp_node = node.change_ext('.pb.cc') + hpp_node = node.change_ext('.pb.h') + self.source.append(cpp_node) + out_nodes.append(cpp_node) + out_nodes.append(hpp_node) + protoc_flags.append('--cpp_out=%s' % node.parent.get_bld().bldpath()) + + if 'py' in self.features: + py_node = node.change_ext('_pb2.py') + self.source.append(py_node) + out_nodes.append(py_node) + protoc_flags.append('--python_out=%s' % node.parent.get_bld().bldpath()) + + if 'javac' in self.features: + pkgname, javapkg, javacn, nodename = None, None, None, None + messages = [] + + # .java file name is done with some rules depending on .proto file content: + # -) package is either derived from option java_package if present + # or from package directive + # -) file name is either derived from option java_outer_classname if present + # or the .proto file is converted to camelcase. If a message + # is named the same then the behaviour depends on protoc version + # + # See also: https://developers.google.com/protocol-buffers/docs/reference/java-generated#invocation + + code = node.read().splitlines() + for line in code: + m = re.search(r'^package\s+(.*);', line) + if m: + pkgname = m.groups()[0] + m = re.search(r'^option\s+(\S*)\s*=\s*"(\S*)";', line) + if m: + optname = m.groups()[0] + if optname == 'java_package': + javapkg = m.groups()[1] + elif optname == 'java_outer_classname': + javacn = m.groups()[1] + if self.env.PROTOC_MAJOR > '2': + m = re.search(r'^message\s+(\w*)\s*{*', line) + if m: + messages.append(m.groups()[0]) + + if javapkg: + nodename = javapkg + elif pkgname: + nodename = pkgname + else: + raise Errors.WafError('Cannot derive java name from protoc file') + + nodename = nodename.replace('.',os.sep) + os.sep + if javacn: + nodename += javacn + '.java' + else: + if self.env.PROTOC_MAJOR > '2' and node.abspath()[node.abspath().rfind(os.sep)+1:node.abspath().rfind('.')].title() in messages: + nodename += node.abspath()[node.abspath().rfind(os.sep)+1:node.abspath().rfind('.')].title() + 'OuterClass.java' + else: + nodename += node.abspath()[node.abspath().rfind(os.sep)+1:node.abspath().rfind('.')].title() + '.java' + + java_node = node.parent.find_or_declare(nodename) + out_nodes.append(java_node) + protoc_flags.append('--java_out=%s' % node.parent.get_bld().bldpath()) + + # Make javac get also pick java code generated in build + if not node.parent.get_bld() in self.javac_task.srcdir: + self.javac_task.srcdir.append(node.parent.get_bld()) + + if not out_nodes: + raise Errors.WafError('Feature %r not supported by protoc extra' % self.features) + + tsk = self.create_task('protoc', node, out_nodes) + tsk.env.append_value('PROTOC_FLAGS', protoc_flags) + + if 'javac' in self.features: + self.javac_task.set_run_after(tsk) + + # Instruct protoc where to search for .proto included files. + # For C++ standard include files dirs are used, + # but this doesn't apply to Python for example + for incpath in getattr(self, 'protoc_includes', []): + incdirs.append(self.bld.path.find_node(incpath).bldpath()) + tsk.env.PROTOC_INCPATHS = incdirs use = getattr(self, 'use', '') if not 'PROTOBUF' in use: self.use = self.to_list(use) + ['PROTOBUF'] def configure(conf): - conf.check_cfg(package="protobuf", uselib_store="PROTOBUF", args=['--cflags', '--libs']) + conf.check_cfg(package='protobuf', uselib_store='PROTOBUF', args=['--cflags', '--libs']) conf.find_program('protoc', var='PROTOC') + conf.start_msg('Checking for protoc version') + protocver = conf.cmd_and_log(conf.env.PROTOC + ['--version'], output=Context.BOTH) + protocver = ''.join(protocver).strip()[protocver[0].rfind(' ')+1:] + conf.end_msg(protocver) + conf.env.PROTOC_MAJOR = protocver[:protocver.find('.')] conf.env.PROTOC_ST = '-I%s' + conf.env.PROTOC_FL = '%s' |