Home | History | Annotate | Download | only in site_tools
      1 '''
      2 Scons Builder for nanopb .proto definitions.
      3 
      4 This tool will locate the nanopb generator and use it to generate .pb.c and
      5 .pb.h files from the .proto files.
      6 
      7 Basic example
      8 -------------
      9 # Build myproto.pb.c and myproto.pb.h from myproto.proto
     10 myproto = env.NanopbProto("myproto")
     11 
     12 # Link nanopb core to the program
     13 env.Append(CPPPATH = "$NANOB")
     14 myprog = env.Program(["myprog.c", myproto, "$NANOPB/pb_encode.c", "$NANOPB/pb_decode.c"])
     15 
     16 Configuration options
     17 ---------------------
     18 Normally, this script is used in the test environment of nanopb and it locates
     19 the nanopb generator by a relative path. If this script is used in another
     20 application, the path to nanopb root directory has to be defined:
     21 
     22 env.SetDefault(NANOPB = "path/to/nanopb")
     23 
     24 Additionally, the path to protoc and the options to give to protoc can be
     25 defined manually:
     26 
     27 env.SetDefault(PROTOC = "path/to/protoc")
     28 env.SetDefault(PROTOCFLAGS = "--plugin=protoc-gen-nanopb=path/to/protoc-gen-nanopb")
     29 '''
     30 
     31 import SCons.Action
     32 import SCons.Builder
     33 import SCons.Util
     34 import os.path
     35 
     36 class NanopbWarning(SCons.Warnings.Warning):
     37     pass
     38 SCons.Warnings.enableWarningClass(NanopbWarning)
     39 
     40 def _detect_nanopb(env):
     41     '''Find the path to nanopb root directory.'''
     42     if env.has_key('NANOPB'):
     43         # Use nanopb dir given by user
     44         return env['NANOPB']
     45     
     46     p = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
     47     if os.path.isdir(p) and os.path.isfile(os.path.join(p, 'pb.h')):
     48         # Assume we are running under tests/site_scons/site_tools
     49         return p
     50     
     51     raise SCons.Errors.StopError(NanopbWarning,
     52         "Could not find the nanopb root directory")
     53 
     54 def _detect_protoc(env):
     55     '''Find the path to the protoc compiler.'''
     56     if env.has_key('PROTOC'):
     57         # Use protoc defined by user
     58         return env['PROTOC']
     59     
     60     n = _detect_nanopb(env)
     61     p1 = os.path.join(n, 'generator-bin', 'protoc' + env['PROGSUFFIX'])
     62     if os.path.exists(p1):
     63         # Use protoc bundled with binary package
     64         return env['ESCAPE'](p1)
     65     
     66     p = env.WhereIs('protoc')
     67     if p:
     68         # Use protoc from path
     69         return env['ESCAPE'](p)
     70     
     71     raise SCons.Errors.StopError(NanopbWarning,
     72         "Could not find the protoc compiler")
     73 
     74 def _detect_protocflags(env):
     75     '''Find the options to use for protoc.'''
     76     if env.has_key('PROTOCFLAGS'):
     77         return env['PROTOCFLAGS']
     78     
     79     p = _detect_protoc(env)
     80     n = _detect_nanopb(env)
     81     p1 = os.path.join(n, 'generator-bin', 'protoc' + env['PROGSUFFIX'])
     82     if p == env['ESCAPE'](p1):
     83         # Using the bundled protoc, no options needed
     84         return ''
     85     
     86     e = env['ESCAPE']
     87     if env['PLATFORM'] == 'win32':
     88         return e('--plugin=protoc-gen-nanopb=' + os.path.join(n, 'generator', 'protoc-gen-nanopb.bat'))
     89     else:
     90         return e('--plugin=protoc-gen-nanopb=' + os.path.join(n, 'generator', 'protoc-gen-nanopb'))
     91 
     92 def _nanopb_proto_actions(source, target, env, for_signature):
     93     esc = env['ESCAPE']
     94     dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
     95     return '$PROTOC $PROTOCFLAGS %s --nanopb_out=. %s' % (dirs, esc(str(source[0])))
     96 
     97 def _nanopb_proto_emitter(target, source, env):
     98     basename = os.path.splitext(str(source[0]))[0]
     99     target.append(basename + '.pb.h')
    100     
    101     if os.path.exists(basename + '.options'):
    102         source.append(basename + '.options')
    103     
    104     return target, source
    105 
    106 _nanopb_proto_builder = SCons.Builder.Builder(
    107     generator = _nanopb_proto_actions,
    108     suffix = '.pb.c',
    109     src_suffix = '.proto',
    110     emitter = _nanopb_proto_emitter)
    111        
    112 def generate(env):
    113     '''Add Builder for nanopb protos.'''
    114     
    115     env['NANOPB'] = _detect_nanopb(env)
    116     env['PROTOC'] = _detect_protoc(env)
    117     env['PROTOCFLAGS'] = _detect_protocflags(env)
    118     
    119     env.SetDefault(PROTOCPATH = ['.', os.path.join(env['NANOPB'], 'generator', 'proto')])
    120     
    121     env.SetDefault(NANOPB_PROTO_CMD = '$PROTOC $PROTOC_OPTS --nanopb_out=. $SOURCE')
    122     env['BUILDERS']['NanopbProto'] = _nanopb_proto_builder
    123     
    124 def exists(env):
    125     return _detect_protoc(env) and _detect_protoc_opts(env)
    126 
    127