1Help('''
2Type 'scons' to build and run all the available test cases.
3It will automatically detect your platform and C compiler and
4build appropriately.
5
6You can modify the behaviour using following options:
7BUILDDIR    Directory to build into (default "build")
8CC          Name of C compiler
9CXX         Name of C++ compiler
10CCFLAGS     Flags to pass to the C compiler
11CXXFLAGS    Flags to pass to the C++ compiler
12LINK        Name of linker (usually same as CC)
13LINKFLAGS   Flags to pass to linker
14LINKLIBS    Flags to pass to linker after object files
15PROTOC      Path to protoc binary
16PROTOCFLAGS Arguments to pass protoc
17NODEFARGS   Do not add the default CCFLAGS
18NOVALGRIND  Do not use valgrind for memory checks
19
20For example, for a clang build, use:
21scons CC=clang CXX=clang++
22''')
23
24import os
25import platform
26env = Environment(ENV = os.environ)
27
28# Allow giving environment flags on command line.
29list_vars = ['CCFLAGS', 'CXXFLAGS', 'LINKFLAGS', 'LINKLIBS', 'PROTOCFLAGS']
30for var,val in ARGUMENTS.items():
31    if var in list_vars:
32        env.Append(**{var: val})
33    else:
34        env.Replace(**{var: val})
35
36env.SetDefault(BUILDDIR = "build")
37env.SConsignFile(env['BUILDDIR'] + "/sconsign")
38env.Replace(CONFIGUREDIR = env['BUILDDIR'] + "/config")
39
40# If a cross compilation platform is given, apply the environment settings
41# from site_scons/platforms/X/X.py
42if ARGUMENTS.get('PLATFORM'):
43    platform_func = platforms.get(ARGUMENTS.get('PLATFORM'))
44    if not platform_func:
45        print("Supported platforms: " + str(platforms.keys()))
46        raise Exception("Unsupported platform: " + ARGUMENTS.get('PLATFORM'))
47    else:
48        platform_func(env)
49
50# Load nanopb generator build rules from site_scons/site_tools/nanopb.py
51env.Tool("nanopb")
52
53# Limit memory usage. This is to catch problems like issue #338
54try:
55    import resource
56    soft, hard = resource.getrlimit(resourse.RLIMIT_AS)
57    resource.setrlimit(resource.RLIMIT_AS, (100*1024*1024, hard))
58except:
59    pass
60
61# On Mac OS X, gcc is usually alias for clang.
62# To avoid problem with default options, use clang directly.
63if platform.system() == "Darwin" and 'CC' not in ARGUMENTS:
64    env.Replace(CC = "clang", CXX = "clang++")
65
66# Add the builders defined in site_init.py
67add_nanopb_builders(env)
68
69# Path to the files shared by tests, and to the nanopb core.
70env.Append(CPPPATH = ["#../", "$COMMON"])
71
72# Path for finding nanopb.proto
73env.Append(PROTOCPATH = '#../generator')
74
75# Check the compilation environment, unless we are just cleaning up.
76if not env.GetOption('clean'):
77    def check_ccflags(context, flags, linkflags = ''):
78        '''Check if given CCFLAGS are supported'''
79        context.Message('Checking support for CCFLAGS="%s" LINKFLAGS="%s"... ' % (flags, linkflags))
80        oldflags = context.env['CCFLAGS']
81        oldlinkflags = context.env['LINKFLAGS']
82        context.env.Append(CCFLAGS = flags)
83        context.env.Append(LINKFLAGS = linkflags)
84        result = context.TryCompile("int main() {return 0;}", '.c')
85        context.env.Replace(CCFLAGS = oldflags)
86        context.env.Replace(LINKFLAGS = oldlinkflags)
87        context.Result(result)
88        return result
89
90    def check_protocversion(context):
91        context.Display("Checking protoc version... ")
92        status, output = context.TryAction('$PROTOC --version > $TARGET')
93        if status:
94            context.Result(str(output.strip()))
95            return str(output.strip())
96        else:
97            context.Display("error: %s\n" % output.strip())
98            context.did_show_result = 1
99            context.Result("unknown, assuming 3.6.1")
100            return "3.6.1"
101
102    conf = Configure(env, custom_tests =
103                     {'CheckCCFLAGS': check_ccflags,
104                      'CheckProtocVersion': check_protocversion})
105
106    # If the platform doesn't support C99, use our own header file instead.
107    stdbool = conf.CheckCHeader('stdbool.h')
108    stdint = conf.CheckCHeader('stdint.h')
109    stddef = conf.CheckCHeader('stddef.h')
110    string = conf.CheckCHeader('string.h')
111    stdlib = conf.CheckCHeader('stdlib.h')
112    limits = conf.CheckCHeader('limits.h')
113    if not stdbool or not stdint or not stddef or not string or not limits:
114        conf.env.Append(CPPDEFINES = {'PB_SYSTEM_HEADER': '\\"pb_syshdr.h\\"'})
115        conf.env.Append(CPPPATH = "#../extra")
116        conf.env.Append(SYSHDR = '\\"pb_syshdr.h\\"')
117
118        if stdbool: conf.env.Append(CPPDEFINES = {'HAVE_STDBOOL_H': 1})
119        if stdint: conf.env.Append(CPPDEFINES = {'HAVE_STDINT_H': 1})
120        if stddef: conf.env.Append(CPPDEFINES = {'HAVE_STDDEF_H': 1})
121        if string: conf.env.Append(CPPDEFINES = {'HAVE_STRING_H': 1})
122        if stdlib: conf.env.Append(CPPDEFINES = {'HAVE_STDLIB_H': 1})
123        if limits: conf.env.Append(CPPDEFINES = {'HAVE_LIMITS_H': 1})
124
125    # Some platforms need libm for isnan()
126    if conf.CheckCCFLAGS('', linkflags = '-lm'):
127        conf.env.Append(LINKFLAGS = '-lm')
128
129    # Check protoc version
130    conf.env['PROTOC_VERSION'] = conf.CheckProtocVersion()
131
132    # Initialize compiler arguments with defaults, unless overridden on
133    # command line.
134    if not env.get('NODEFARGS'):
135        # Check if libmudflap is available (only with GCC)
136        if 'gcc' in env['CC']:
137            if conf.CheckLib('mudflap'):
138                conf.env.Append(CCFLAGS = '-fmudflap')
139                conf.env.Append(LINKFLAGS = '-fmudflap')
140
141        # Check if we can use extra strict warning flags (only with GCC)
142        extra = '-Wcast-qual -Wlogical-op -Wconversion'
143        extra += ' -fstrict-aliasing -Wstrict-aliasing=1'
144        extra += ' -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls'
145        extra += ' -Wstack-protector '
146        if 'gcc' in env['CC']:
147            if conf.CheckCCFLAGS(extra):
148                conf.env.Append(CORECFLAGS = extra)
149
150        # Check if we can use undefined behaviour sanitizer (only with clang)
151        # TODO: Fuzz test triggers the bool sanitizer, figure out whether to
152        #       modify the fuzz test or to keep ignoring the check.
153        extra = '-fsanitize=undefined,integer -fno-sanitize-recover=undefined,integer '
154        if 'clang' in env['CC']:
155            if conf.CheckCCFLAGS(extra, linkflags = extra):
156                conf.env.Append(CORECFLAGS = extra)
157                conf.env.Append(LINKFLAGS = extra)
158
159    # End the config stuff
160    env = conf.Finish()
161
162if not env.get('NODEFARGS'):
163    # Initialize the CCFLAGS according to the compiler
164    if 'gcc' in env['CC']:
165        # GNU Compiler Collection
166
167        # Debug info, warnings as errors
168        env.Append(CFLAGS = '-g -Wall -Werror ')
169        env.Append(CORECFLAGS = '-Wextra ')
170
171        # Pedantic ANSI C. On AVR this doesn't work because we use large
172        # enums in some of the tests.
173        if env.get("EMBEDDED") != "AVR":
174            env.Append(CFLAGS = '-ansi ')
175            env.Append(CORECFLAGS = '-pedantic ')
176
177        # Profiling and coverage
178        if not env.get("EMBEDDED"):
179            env.Append(CFLAGS = '-fprofile-arcs -ftest-coverage ')
180            env.Append(LINKFLAGS = '-g --coverage')
181
182
183        # We currently need uint64_t anyway, even though ANSI C90 otherwise..
184        env.Append(CFLAGS = '-Wno-long-long')
185    elif 'clang' in env['CC']:
186        # CLang
187        env.Append(CFLAGS = '-ansi -g -Wall -Werror')
188        env.Append(CORECFLAGS = ' -Wextra -Wcast-qual -Wconversion')
189    elif 'cl' in env['CC']:
190        # Microsoft Visual C++
191
192        # Debug info on, warning level 2 for tests, warnings as errors
193        env.Append(CFLAGS = '/Zi /W2 /WX')
194        env.Append(LINKFLAGS = '/DEBUG')
195
196        # More strict checks on the nanopb core
197        env.Append(CORECFLAGS = '/W4')
198
199        # Enable C11 standard
200        env.Append(CFLAGS = ' /std:c11 ')
201
202        # Disable warning about sizeof(union{}) construct that is used in
203        # message size macros, in e.g. multiple_files testcase. The C construct
204        # itself is valid, but quite rare, which causes Visual C++ to give a warning
205        # about it.
206        env.Append(CFLAGS = '/wd4116')
207    elif 'tcc' in env['CC']:
208        # Tiny C Compiler
209        env.Append(CFLAGS = '-Wall -Werror -g')
210
211    if 'clang' in env['CXX']:
212        env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
213    elif 'g++' in env['CXX'] or 'gcc' in env['CXX']:
214        env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers -std=gnu++11')
215    elif 'cl' in env['CXX']:
216        env.Append(CXXFLAGS = '/Zi /W2 /WX /wd4116 /wd4127')
217
218env.SetDefault(CORECFLAGS = '')
219
220if not env.get("EMBEDDED") and not env.get("NOVALGRIND"):
221    valgrind = env.WhereIs('valgrind')
222    if valgrind:
223        env.SetDefault(VALGRIND = valgrind)
224
225# Make it possible to add commands to the end of linker line
226env.SetDefault(LINKLIBS = '')
227env.Replace(LINKCOM = env['LINKCOM'] + " $LINKLIBS")
228
229# Place build files under separate folder
230import os.path
231env['VARIANT_DIR'] = env['BUILDDIR']
232env['BUILD'] = '#' + env['VARIANT_DIR']
233env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common'
234
235# Include common/SConscript first to make sure its exports are available
236# to other SConscripts.
237SConscript("common/SConscript", exports = 'env', variant_dir = env['VARIANT_DIR'] + '/common')
238
239# Now include the SConscript files from all subdirectories
240for subdir in Glob('*/SConscript') + Glob('regression/*/SConscript'):
241    if str(subdir).startswith("common/"): continue
242    if str(subdir).startswith("common\\"): continue
243    SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir)))
244
245