1''' 2Scons Builder for nanopb .proto definitions. 3 4This tool will locate the nanopb generator and use it to generate .pb.c and 5.pb.h files from the .proto files. 6 7Basic example 8------------- 9# Build myproto.pb.c and myproto.pb.h from myproto.proto 10myproto = env.NanopbProto("myproto") 11 12# Link nanopb core to the program 13env.Append(CPPPATH = "$NANOB") 14myprog = env.Program(["myprog.c", myproto, "$NANOPB/pb_encode.c", "$NANOPB/pb_decode.c"]) 15 16Configuration options 17--------------------- 18Normally, this script is used in the test environment of nanopb and it locates 19the nanopb generator by a relative path. If this script is used in another 20application, the path to nanopb root directory has to be defined: 21 22env.SetDefault(NANOPB = "path/to/nanopb") 23 24Additionally, the path to protoc and the options to give to protoc can be 25defined manually: 26 27env.SetDefault(PROTOC = "path/to/protoc") 28env.SetDefault(PROTOCFLAGS = "--plugin=protoc-gen-nanopb=path/to/protoc-gen-nanopb") 29''' 30 31import SCons.Action 32import SCons.Builder 33import SCons.Util 34import os.path 35 36class NanopbWarning(SCons.Warnings.Warning): 37 pass 38SCons.Warnings.enableWarningClass(NanopbWarning) 39 40def _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 54def _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 74def _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 92def _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 97def _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 112def 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 $PROTOCFLAGS --nanopb_out=. $SOURCES') 122 env['BUILDERS']['NanopbProto'] = _nanopb_proto_builder 123 124def exists(env): 125 return _detect_protoc(env) and _detect_protoc_opts(env) 126 127