Generating Protocol Buffers files with SCons
This builder will allow you to generate Google Protocol Buffers .pb.cc, .pb.h, and _pb2.py files using SCons from a .proto source file.
It doesn't currently support Java, but that should be easy to add.
Put this builder/tool file somewhere (next to the SConstruct file or in the Environment's 'toolpath', and call it 'protoc.py'.
1 """
2 protoc.py: Protoc Builder for SCons
3
4 This Builder invokes protoc to generate C++ and Python
5 from a .proto file.
6
7 NOTE: Java is not currently supported."""
8
9 __author__ = "Scott Stafford"
10
11 import SCons.Action
12 import SCons.Builder
13 import SCons.Defaults
14 import SCons.Node.FS
15 import SCons.Util
16
17 from SCons.Script import File, Dir
18
19 import os.path
20
21 protocs = 'protoc'
22
23 ProtocAction = SCons.Action.Action('$PROTOCCOM', '$PROTOCCOMSTR')
24 def ProtocEmitter(target, source, env):
25 dirOfCallingSConscript = Dir('.').srcnode()
26 env.Prepend(PROTOCPROTOPATH = dirOfCallingSConscript.path)
27
28 source_with_corrected_path = []
29 for src in source:
30 commonprefix = os.path.commonprefix([dirOfCallingSConscript.path, src.srcnode().path])
31 if len(commonprefix)>0:
32 source_with_corrected_path.append( src.srcnode().path[len(commonprefix + os.sep):] )
33 else:
34 source_with_corrected_path.append( src.srcnode().path )
35
36 source = source_with_corrected_path
37
38 for src in source:
39 modulename = os.path.splitext(src)[0]
40
41 if env['PROTOCOUTDIR']:
42 base = os.path.join(env['PROTOCOUTDIR'] , modulename)
43 target.extend( [ base + '.pb.cc', base + '.pb.h' ] )
44
45 if env['PROTOCPYTHONOUTDIR']:
46 base = os.path.join(env['PROTOCPYTHONOUTDIR'] , modulename)
47 target.append( base + '_pb2.py' )
48
49 try:
50 target.append(env['PROTOCFDSOUT'])
51 except KeyError:
52 pass
53
54 #~ print "PROTOC SOURCE:", [str(s) for s in source]
55 #~ print "PROTOC TARGET:", [str(s) for s in target]
56
57 return target, source
58
59 ProtocBuilder = SCons.Builder.Builder(action = ProtocAction,
60 emitter = ProtocEmitter,
61 srcsuffix = '$PROTOCSRCSUFFIX')
62
63 def generate(env):
64 """Add Builders and construction variables for protoc to an Environment."""
65 try:
66 bld = env['BUILDERS']['Protoc']
67 except KeyError:
68 bld = ProtocBuilder
69 env['BUILDERS']['Protoc'] = bld
70
71 env['PROTOC'] = env.Detect(protocs) or 'protoc'
72 env['PROTOCFLAGS'] = SCons.Util.CLVar('')
73 env['PROTOCPROTOPATH'] = SCons.Util.CLVar('')
74 env['PROTOCCOM'] = '$PROTOC ${["-I%s"%x for x in PROTOCPROTOPATH]} $PROTOCFLAGS --cpp_out=$PROTOCCPPOUTFLAGS$PROTOCOUTDIR ${PROTOCPYTHONOUTDIR and ("--python_out="+PROTOCPYTHONOUTDIR) or ""} ${PROTOCFDSOUT and ("-o"+PROTOCFDSOUT) or ""} ${SOURCES}'
75 env['PROTOCOUTDIR'] = '${SOURCE.dir}'
76 env['PROTOCPYTHONOUTDIR'] = "python"
77 env['PROTOCSRCSUFFIX'] = '.proto'
78
79 def exists(env):
80 return env.Detect(protocs)
Now you should have an env.Protoc() builder that takes .proto as source and emits generated protobuf source.
Here is a sample SConstruct I used (some MSVC/Windows-specific stuff, but the Protoc builder is OS-agnostic.) Note that you must make sure the protoc executable is available in your PATH or it won't be able to execute it.
1 import os
2
3 env = Environment(
4 ENV = {'PATH' : os.environ['PATH']},
5 CPPPATH = ['C:\wc\protobuf-trunk\src' ],
6 CPPFLAGS = ['/EHsc', '/MDd'],
7 tools=['default', 'protoc']
8 )
9
10 proto_files = env.Protoc(
11 [],
12 "config.proto",
13 PROTOCPROTOPATH=['.',r'C:\wc\protobuf-trunk\src',],
14 PROTOCPYTHONOUTDIR='python', # set to None to not generate python
15 PROTOCOUTDIR = 'build', # defaults to same directory as .proto
16 # PROTOCCPPOUTFLAGS = "dllexport_decl=PROTOCONFIG_EXPORT:", too
17 )
18
19 env.Program('test', ['test.cpp', proto_files[0]],
20 LIBPATH=[r'C:\wc\protobuf-trunk\vsprojects\Debug'],
21 LIBS=['libprotobuf.lib'],
22 )
