summaryrefslogtreecommitdiffstats
path: root/tests/SConstruct
blob: 7a2784432cabf249e9f4dfdda651a9ee1f34f05b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
Help('''
Type 'scons' to build and run all the available test cases.
It will automatically detect your platform and C compiler and
build appropriately.

You can modify the behavious using following options:
CC          Name of C compiler
CXX         Name of C++ compiler
CCFLAGS     Flags to pass to the C compiler
CXXFLAGS    Flags to pass to the C++ compiler

For example, for a clang build, use:
scons CC=clang CXX=clang++
''')

import os
env = Environment(ENV = os.environ, tools = ['default', 'nanopb'])

# Allow overriding the compiler with scons CC=???
if 'CC' in ARGUMENTS: env.Replace(CC = ARGUMENTS['CC'])
if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX'])
if 'CCFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CCFLAGS'])
if 'CXXFLAGS' in ARGUMENTS: env.Append(CXXFLAGS = ARGUMENTS['CXXFLAGS'])

# Add the builders defined in site_init.py
add_nanopb_builders(env)

# Path to the files shared by tests, and to the nanopb core.
env.Append(CPPPATH = ["#../", "$COMMON"])

# Path for finding nanopb.proto
env.Append(PROTOCPATH = '#../generator')

# Check the compilation environment, unless we are just cleaning up.
if not env.GetOption('clean'):
    def check_ccflags(context, flags, linkflags = ''):
        '''Check if given CCFLAGS are supported'''
        context.Message('Checking support for CCFLAGS="%s"... ' % flags)
        oldflags = context.env['CCFLAGS']
        oldlinkflags = context.env['CCFLAGS']
        context.env.Append(CCFLAGS = flags)
        context.env.Append(LINKFLAGS = linkflags)
        result = context.TryCompile("int main() {return 0;}", '.c')
        context.env.Replace(CCFLAGS = oldflags)
        context.env.Replace(LINKFLAGS = oldlinkflags)
        context.Result(result)
        return result
    
    conf = Configure(env, custom_tests = {'CheckCCFLAGS': check_ccflags})

    # If the platform doesn't support C99, use our own header file instead.
    stdbool = conf.CheckCHeader('stdbool.h')
    stdint = conf.CheckCHeader('stdint.h')
    stddef = conf.CheckCHeader('stddef.h')
    string = conf.CheckCHeader('string.h')
    stdlib = conf.CheckCHeader('stdlib.h')
    if not stdbool or not stdint or not stddef or not string:
        conf.env.Append(CPPDEFINES = {'PB_SYSTEM_HEADER': '\\"pb_syshdr.h\\"'})
        conf.env.Append(CPPPATH = "#../extra")
        
        if stdbool: conf.env.Append(CPPDEFINES = {'HAVE_STDBOOL_H': 1})
        if stdint: conf.env.Append(CPPDEFINES = {'HAVE_STDINT_H': 1})
        if stddef: conf.env.Append(CPPDEFINES = {'HAVE_STDDEF_H': 1})
        if string: conf.env.Append(CPPDEFINES = {'HAVE_STRING_H': 1})
        if stdlib: conf.env.Append(CPPDEFINES = {'HAVE_STDLIB_H': 1})
    
    # Check if we can use pkg-config to find protobuf include path
    status, output = conf.TryAction('pkg-config protobuf --variable=includedir > $TARGET')
    if status:
        conf.env.Append(PROTOCPATH = output.strip())
    else:
        conf.env.Append(PROTOCPATH = '/usr/include')
    
    # Check if libmudflap is available (only with GCC)
    if 'gcc' in env['CC']:
        if conf.CheckLib('mudflap'):
            conf.env.Append(CCFLAGS = '-fmudflap')
            conf.env.Append(LINKFLAGS = '-fmudflap')
    
    # Check if we can use extra strict warning flags (only with GCC)
    extra = '-Wcast-qual -Wlogical-op -Wconversion'
    extra += ' -fstrict-aliasing -Wstrict-aliasing=1'
    extra += ' -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls'
    extra += ' -Wstack-protector '
    if 'gcc' in env['CC']:
        if conf.CheckCCFLAGS(extra):
            conf.env.Append(CORECFLAGS = extra)
    
    # Check if we can use undefined behaviour sanitizer (only with clang)
    extra = '-fsanitize=undefined '
    if 'clang' in env['CC']:
        if conf.CheckCCFLAGS(extra, linkflags = extra):
            conf.env.Append(CORECFLAGS = extra)
            conf.env.Append(LINKFLAGS = extra)
    
    # End the config stuff
    env = conf.Finish()

# Initialize the CCFLAGS according to the compiler
if 'gcc' in env['CC']:
    # GNU Compiler Collection
    
    # Debug info, warnings as errors
    env.Append(CFLAGS = '-ansi -pedantic -g -Wall -Werror -fprofile-arcs -ftest-coverage -fstack-protector-all')
    env.Append(CORECFLAGS = '-Wextra')
    env.Append(LINKFLAGS = '-g --coverage')
    
    # We currently need uint64_t anyway, even though ANSI C90 otherwise..
    env.Append(CFLAGS = '-Wno-long-long')
elif 'clang' in env['CC']:
    # CLang
    env.Append(CFLAGS = '-ansi -g -Wall -Werror')
    env.Append(CORECFLAGS = ' -Wextra -Wcast-qual -Wconversion')
elif 'cl' in env['CC']:
    # Microsoft Visual C++
    
    # Debug info on, warning level 2 for tests, warnings as errors
    env.Append(CFLAGS = '/Zi /W2 /WX')
    env.Append(LINKFLAGS = '/DEBUG')
    
    # More strict checks on the nanopb core
    env.Append(CORECFLAGS = '/W4')
    
    # PB_RETURN_ERROR triggers C4127 because of while(0)
    env.Append(CFLAGS = '/wd4127')
elif 'tcc' in env['CC']:
    # Tiny C Compiler
    env.Append(CFLAGS = '-Wall -Werror -g')

env.SetDefault(CORECFLAGS = '')

if 'clang' in env['CXX']:
    env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
elif 'g++' in env['CXX'] or 'gcc' in env['CXX']:
    env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
elif 'cl' in env['CXX']:
    env.Append(CXXFLAGS = '/Zi /W2 /WX')
    
# Now include the SConscript files from all subdirectories
import os.path
env['VARIANT_DIR'] = 'build'
env['BUILD'] = '#' + env['VARIANT_DIR']
env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common'
for subdir in Glob('*/SConscript'):
    SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir)))