waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

protoc.py (6873B)


      1 #!/usr/bin/env python
      2 # encoding: utf-8
      3 # Philipp Bender, 2012
      4 # Matt Clarkson, 2012
      5 
      6 import re, os
      7 from waflib.Task import Task
      8 from waflib.TaskGen import extension
      9 from waflib import Errors, Context, Logs
     10 
     11 """
     12 A simple tool to integrate protocol buffers into your build system.
     13 
     14 Example for C++:
     15 
     16     def configure(conf):
     17         conf.load('compiler_cxx cxx protoc')
     18 
     19     def build(bld):
     20         bld(
     21                 features = 'cxx cxxprogram'
     22                 source   = 'main.cpp file1.proto proto/file2.proto',
     23                 includes = '. proto',
     24                 target   = 'executable')
     25 
     26 Example for Python:
     27 
     28     def configure(conf):
     29         conf.load('python protoc')
     30 
     31     def build(bld):
     32         bld(
     33                 features = 'py'
     34                 source   = 'main.py file1.proto proto/file2.proto',
     35                 protoc_includes  = 'proto')
     36 
     37 Example for both Python and C++ at same time:
     38 
     39     def configure(conf):
     40         conf.load('cxx python protoc')
     41 
     42     def build(bld):
     43         bld(
     44                 features = 'cxx py'
     45                 source   = 'file1.proto proto/file2.proto',
     46                 protoc_includes  = 'proto')	# or includes
     47 
     48 
     49 Example for Java:
     50 
     51     def options(opt):
     52         opt.load('java')
     53 
     54     def configure(conf):
     55         conf.load('python java protoc')
     56         # Here you have to point to your protobuf-java JAR and have it in classpath
     57         conf.env.CLASSPATH_PROTOBUF = ['protobuf-java-2.5.0.jar']
     58 
     59     def build(bld):
     60         bld(
     61                 features = 'javac protoc',
     62                 name = 'pbjava',
     63                 srcdir = 'inc/ src',	# directories used by javac
     64                 source   = ['inc/message_inc.proto', 'inc/message.proto'],
     65 					# source is used by protoc for .proto files
     66                 use = 'PROTOBUF',
     67                 protoc_includes = ['inc']) # for protoc to search dependencies
     68 
     69 
     70 Protoc includes passed via protoc_includes are either relative to the taskgen
     71 or to the project and are searched in this order.
     72 
     73 Include directories external to the waf project can also be passed to the
     74 extra by using protoc_extincludes
     75 
     76                 protoc_extincludes = ['/usr/include/pblib']
     77 
     78 
     79 Notes when using this tool:
     80 
     81 - protoc command line parsing is tricky.
     82 
     83   The generated files can be put in subfolders which depend on
     84   the order of the include paths.
     85 
     86   Try to be simple when creating task generators
     87   containing protoc stuff.
     88 
     89 """
     90 
     91 class protoc(Task):
     92 	run_str = '${PROTOC} ${PROTOC_FL:PROTOC_FLAGS} ${PROTOC_ST:INCPATHS} ${PROTOC_ST:PROTOC_INCPATHS} ${PROTOC_ST:PROTOC_EXTINCPATHS} ${SRC[0].bldpath()}'
     93 	color   = 'BLUE'
     94 	ext_out = ['.h', 'pb.cc', '.py', '.java']
     95 	def scan(self):
     96 		"""
     97 		Scan .proto dependencies
     98 		"""
     99 		node = self.inputs[0]
    100 
    101 		nodes = []
    102 		names = []
    103 		seen = []
    104 		search_nodes = []
    105 
    106 		if not node:
    107 			return (nodes, names)
    108 
    109 		if 'cxx' in self.generator.features:
    110 			search_nodes = self.generator.includes_nodes
    111 
    112 		if 'py' in self.generator.features or 'javac' in self.generator.features:
    113 			for incpath in getattr(self.generator, 'protoc_includes', []):
    114 				incpath_node = self.generator.path.find_node(incpath)
    115 				if incpath_node:
    116 					search_nodes.append(incpath_node)
    117 				else:
    118 					# Check if relative to top-level for extra tg dependencies
    119 					incpath_node = self.generator.bld.path.find_node(incpath)
    120 					if incpath_node:
    121 						search_nodes.append(incpath_node)
    122 					else:
    123 						raise Errors.WafError('protoc: include path %r does not exist' % incpath)
    124 
    125 
    126 		def parse_node(node):
    127 			if node in seen:
    128 				return
    129 			seen.append(node)
    130 			code = node.read().splitlines()
    131 			for line in code:
    132 				m = re.search(r'^import\s+"(.*)";.*(//)?.*', line)
    133 				if m:
    134 					dep = m.groups()[0]
    135 					for incnode in search_nodes:
    136 						found = incnode.find_resource(dep)
    137 						if found:
    138 							nodes.append(found)
    139 							parse_node(found)
    140 						else:
    141 							names.append(dep)
    142 
    143 		parse_node(node)
    144 		# Add also dependencies path to INCPATHS so protoc will find the included file
    145 		for deppath in nodes:
    146 			self.env.append_unique('INCPATHS', deppath.parent.bldpath())
    147 		return (nodes, names)
    148 
    149 @extension('.proto')
    150 def process_protoc(self, node):
    151 	incdirs = []
    152 	out_nodes = []
    153 	protoc_flags = []
    154 
    155 	# ensure PROTOC_FLAGS is a list; a copy is used below anyway
    156 	self.env.PROTOC_FLAGS = self.to_list(self.env.PROTOC_FLAGS)
    157 
    158 	if 'cxx' in self.features:
    159 		cpp_node = node.change_ext('.pb.cc')
    160 		hpp_node = node.change_ext('.pb.h')
    161 		self.source.append(cpp_node)
    162 		out_nodes.append(cpp_node)
    163 		out_nodes.append(hpp_node)
    164 		protoc_flags.append('--cpp_out=%s' % node.parent.get_bld().bldpath())
    165 
    166 	if 'py' in self.features:
    167 		py_node = node.change_ext('_pb2.py')
    168 		self.source.append(py_node)
    169 		out_nodes.append(py_node)
    170 		protoc_flags.append('--python_out=%s' % node.parent.get_bld().bldpath())
    171 
    172 	if 'javac' in self.features:
    173 		# Make javac get also pick java code generated in build
    174 		if not node.parent.get_bld() in self.javac_task.srcdir:
    175 			self.javac_task.srcdir.append(node.parent.get_bld())
    176 
    177 		protoc_flags.append('--java_out=%s' % node.parent.get_bld().bldpath())
    178 		node.parent.get_bld().mkdir()
    179 
    180 	tsk = self.create_task('protoc', node, out_nodes)
    181 	tsk.env.append_value('PROTOC_FLAGS', protoc_flags)
    182 
    183 	if 'javac' in self.features:
    184 		self.javac_task.set_run_after(tsk)
    185 
    186 	# Instruct protoc where to search for .proto included files.
    187 	# For C++ standard include files dirs are used,
    188 	# but this doesn't apply to Python for example
    189 	for incpath in getattr(self, 'protoc_includes', []):
    190 		incpath_node = self.path.find_node(incpath)
    191 		if incpath_node:
    192 			incdirs.append(incpath_node.bldpath())
    193 		else:
    194 			# Check if relative to top-level for extra tg dependencies
    195 			incpath_node = self.bld.path.find_node(incpath)
    196 			if incpath_node:
    197 				incdirs.append(incpath_node.bldpath())
    198 			else:
    199 				raise Errors.WafError('protoc: include path %r does not exist' % incpath)
    200 
    201 	tsk.env.PROTOC_INCPATHS = incdirs
    202 
    203 	# Include paths external to the waf project (ie. shared pb repositories)
    204 	tsk.env.PROTOC_EXTINCPATHS = getattr(self, 'protoc_extincludes', [])
    205 
    206 	# PR2115: protoc generates output of .proto files in nested
    207 	# directories  by canonicalizing paths. To avoid this we have to pass
    208 	# as first include the full directory file of the .proto file
    209 	tsk.env.prepend_value('INCPATHS', node.parent.bldpath())
    210 
    211 	use = getattr(self, 'use', '')
    212 	if not 'PROTOBUF' in use:
    213 		self.use = self.to_list(use) + ['PROTOBUF']
    214 
    215 def configure(conf):
    216 	conf.check_cfg(package='protobuf', uselib_store='PROTOBUF', args=['--cflags', '--libs'])
    217 	conf.find_program('protoc', var='PROTOC')
    218 	conf.start_msg('Checking for protoc version')
    219 	protocver = conf.cmd_and_log(conf.env.PROTOC + ['--version'], output=Context.BOTH)
    220 	protocver = ''.join(protocver).strip()[protocver[0].rfind(' ')+1:]
    221 	conf.end_msg(protocver)
    222 	conf.env.PROTOC_MAJOR = protocver[:protocver.find('.')]
    223 	conf.env.PROTOC_ST = '-I%s'
    224 	conf.env.PROTOC_FL = '%s'