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 behavious 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"... ' % flags)
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    # Check protoc version
126    conf.env['PROTOC_VERSION'] = conf.CheckProtocVersion()
127
128    # Initialize compiler arguments with defaults, unless overridden on
129    # command line.
130    if not env.get('NODEFARGS'):
131        # Check if libmudflap is available (only with GCC)
132        if 'gcc' in env['CC']:
133            if conf.CheckLib('mudflap'):
134                conf.env.Append(CCFLAGS = '-fmudflap')
135                conf.env.Append(LINKFLAGS = '-fmudflap')
136
137        # Check if we can use extra strict warning flags (only with GCC)
138        extra = '-Wcast-qual -Wlogical-op -Wconversion'
139        extra += ' -fstrict-aliasing -Wstrict-aliasing=1'
140        extra += ' -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls'
141        extra += ' -Wstack-protector '
142        if 'gcc' in env['CC']:
143            if conf.CheckCCFLAGS(extra):
144                conf.env.Append(CORECFLAGS = extra)
145
146        # Check if we can use undefined behaviour sanitizer (only with clang)
147        # TODO: Fuzz test triggers the bool sanitizer, figure out whether to
148        #       modify the fuzz test or to keep ignoring the check.
149        extra = '-fsanitize=undefined,integer -fno-sanitize-recover=undefined,integer '
150        if 'clang' in env['CC']:
151            if conf.CheckCCFLAGS(extra, linkflags = extra):
152                conf.env.Append(CORECFLAGS = extra)
153                conf.env.Append(LINKFLAGS = extra)
154
155    # End the config stuff
156    env = conf.Finish()
157
158if not env.get('NODEFARGS'):
159    # Initialize the CCFLAGS according to the compiler
160    if 'gcc' in env['CC']:
161        # GNU Compiler Collection
162
163        # Debug info, warnings as errors
164        env.Append(CFLAGS = '-g -Wall -Werror ')
165        env.Append(CORECFLAGS = '-Wextra')
166
167        # Pedantic ANSI C. On AVR this doesn't work because we use large
168        # enums in some of the tests.
169        if env.get("EMBEDDED") != "AVR":
170            env.Append(CFLAGS = '-ansi -pedantic')
171
172        # Profiling and coverage
173        if not env.get("EMBEDDED"):
174            env.Append(CFLAGS = '-fprofile-arcs -ftest-coverage ')
175            env.Append(LINKFLAGS = '-g --coverage')
176
177
178        # We currently need uint64_t anyway, even though ANSI C90 otherwise..
179        env.Append(CFLAGS = '-Wno-long-long')
180    elif 'clang' in env['CC']:
181        # CLang
182        env.Append(CFLAGS = '-ansi -g -Wall -Werror')
183        env.Append(CORECFLAGS = ' -Wextra -Wcast-qual -Wconversion')
184    elif 'cl' in env['CC']:
185        # Microsoft Visual C++
186
187        # Debug info on, warning level 2 for tests, warnings as errors
188        env.Append(CFLAGS = '/Zi /W2 /WX')
189        env.Append(LINKFLAGS = '/DEBUG')
190
191        # More strict checks on the nanopb core
192        env.Append(CORECFLAGS = '/W4')
193
194        # Disable warning about sizeof(union{}) construct that is used in
195        # message size macros, in e.g. multiple_files testcase. The C construct
196        # itself is valid, but quite rare, which causes Visual C++ to give a warning
197        # about it.
198        env.Append(CFLAGS = '/wd4116')
199    elif 'tcc' in env['CC']:
200        # Tiny C Compiler
201        env.Append(CFLAGS = '-Wall -Werror -g')
202
203    if 'clang' in env['CXX']:
204        env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
205    elif 'g++' in env['CXX'] or 'gcc' in env['CXX']:
206        env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
207    elif 'cl' in env['CXX']:
208        env.Append(CXXFLAGS = '/Zi /W2 /WX /wd4116 /wd4127')
209
210env.SetDefault(CORECFLAGS = '')
211
212if not env.get("EMBEDDED") and not env.get("NOVALGRIND"):
213    valgrind = env.WhereIs('valgrind')
214    if valgrind:
215        env.SetDefault(VALGRIND = valgrind)
216
217# Make it possible to add commands to the end of linker line
218env.SetDefault(LINKLIBS = '')
219env.Replace(LINKCOM = env['LINKCOM'] + " $LINKLIBS")
220
221# Place build files under separate folder
222import os.path
223env['VARIANT_DIR'] = env['BUILDDIR']
224env['BUILD'] = '#' + env['VARIANT_DIR']
225env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common'
226
227# Include common/SConscript first to make sure its exports are available
228# to other SConscripts.
229SConscript("common/SConscript", exports = 'env', variant_dir = env['VARIANT_DIR'] + '/common')
230
231# Now include the SConscript files from all subdirectories
232for subdir in Glob('*/SConscript') + Glob('regression/*/SConscript'):
233    if str(subdir).startswith("common/"): continue
234    if str(subdir).startswith("common\\"): continue
235    SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir)))
236
237