17ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen'''
27ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenScons Builder for nanopb .proto definitions.
37ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
47ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenThis tool will locate the nanopb generator and use it to generate .pb.c and
57ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen.pb.h files from the .proto files.
67ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
77ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenBasic example
87ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen-------------
97ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# Build myproto.pb.c and myproto.pb.h from myproto.proto
107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenmyproto = env.NanopbProto("myproto")
117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen# Link nanopb core to the program
137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenenv.Append(CPPPATH = "$NANOB")
147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenmyprog = env.Program(["myprog.c", myproto, "$NANOPB/pb_encode.c", "$NANOPB/pb_decode.c"])
157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenConfiguration options
177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen---------------------
187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenNormally, this script is used in the test environment of nanopb and it locates
197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenthe nanopb generator by a relative path. If this script is used in another
207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenapplication, the path to nanopb root directory has to be defined:
217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenenv.SetDefault(NANOPB = "path/to/nanopb")
237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenAdditionally, the path to protoc and the options to give to protoc can be
257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendefined manually:
267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
277ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenenv.SetDefault(PROTOC = "path/to/protoc")
287ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenenv.SetDefault(PROTOCFLAGS = "--plugin=protoc-gen-nanopb=path/to/protoc-gen-nanopb")
297ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen'''
307ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
317ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport SCons.Action
327ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport SCons.Builder
337ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport SCons.Util
347ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenimport os.path
357ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
367ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohenclass NanopbWarning(SCons.Warnings.Warning):
377ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    pass
387ef855e462b9a18b7d330e4b40f350164a6ad9daEtan CohenSCons.Warnings.enableWarningClass(NanopbWarning)
397ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
407ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef _detect_nanopb(env):
417ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Find the path to nanopb root directory.'''
427ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if env.has_key('NANOPB'):
437ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Use nanopb dir given by user
447ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return env['NANOPB']
457ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
467ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    p = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
477ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if os.path.isdir(p) and os.path.isfile(os.path.join(p, 'pb.h')):
487ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Assume we are running under tests/site_scons/site_tools
497ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return p
507ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
517ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    raise SCons.Errors.StopError(NanopbWarning,
527ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        "Could not find the nanopb root directory")
537ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
547ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef _detect_protoc(env):
557ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Find the path to the protoc compiler.'''
567ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if env.has_key('PROTOC'):
577ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Use protoc defined by user
587ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return env['PROTOC']
597ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
607ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    n = _detect_nanopb(env)
617ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    p1 = os.path.join(n, 'generator-bin', 'protoc' + env['PROGSUFFIX'])
627ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if os.path.exists(p1):
637ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Use protoc bundled with binary package
647ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return env['ESCAPE'](p1)
657ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
667ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    p = env.WhereIs('protoc')
677ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if p:
687ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Use protoc from path
697ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return env['ESCAPE'](p)
707ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
717ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    raise SCons.Errors.StopError(NanopbWarning,
727ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        "Could not find the protoc compiler")
737ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
747ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef _detect_protocflags(env):
757ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Find the options to use for protoc.'''
767ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if env.has_key('PROTOCFLAGS'):
777ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return env['PROTOCFLAGS']
787ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
797ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    p = _detect_protoc(env)
807ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    n = _detect_nanopb(env)
817ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    p1 = os.path.join(n, 'generator-bin', 'protoc' + env['PROGSUFFIX'])
827ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if p == env['ESCAPE'](p1):
837ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        # Using the bundled protoc, no options needed
847ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return ''
857ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
867ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    e = env['ESCAPE']
877ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if env['PLATFORM'] == 'win32':
887ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return e('--plugin=protoc-gen-nanopb=' + os.path.join(n, 'generator', 'protoc-gen-nanopb.bat'))
897ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    else:
907ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        return e('--plugin=protoc-gen-nanopb=' + os.path.join(n, 'generator', 'protoc-gen-nanopb'))
917ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
927ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef _nanopb_proto_actions(source, target, env, for_signature):
937ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    esc = env['ESCAPE']
947ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
957ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return '$PROTOC $PROTOCFLAGS %s --nanopb_out=. %s' % (dirs, esc(str(source[0])))
967ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
977ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef _nanopb_proto_emitter(target, source, env):
987ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    basename = os.path.splitext(str(source[0]))[0]
997ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    target.append(basename + '.pb.h')
1007ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1017ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    if os.path.exists(basename + '.options'):
1027ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen        source.append(basename + '.options')
1037ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1047ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return target, source
1057ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1067ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen_nanopb_proto_builder = SCons.Builder.Builder(
1077ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    generator = _nanopb_proto_actions,
1087ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    suffix = '.pb.c',
1097ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    src_suffix = '.proto',
1107ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    emitter = _nanopb_proto_emitter)
1117ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1127ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef generate(env):
1137ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    '''Add Builder for nanopb protos.'''
1147ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1157ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    env['NANOPB'] = _detect_nanopb(env)
1167ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    env['PROTOC'] = _detect_protoc(env)
1177ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    env['PROTOCFLAGS'] = _detect_protocflags(env)
1187ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1197ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    env.SetDefault(PROTOCPATH = ['.', os.path.join(env['NANOPB'], 'generator', 'proto')])
1207ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1217ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    env.SetDefault(NANOPB_PROTO_CMD = '$PROTOC $PROTOC_OPTS --nanopb_out=. $SOURCE')
1227ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    env['BUILDERS']['NanopbProto'] = _nanopb_proto_builder
1237ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
1247ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohendef exists(env):
1257ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen    return _detect_protoc(env) and _detect_protoc_opts(env)
1267ef855e462b9a18b7d330e4b40f350164a6ad9daEtan Cohen
127