1#!/usr/bin/python
2
3"""
4This configures and builds with many different sub-configurations
5in an attempt to flush out missing feature macro checks, etc.
6
7There is probably no reason for you to run this. Unless you want to.
8
9(C) 2017 Jack Lloyd
10
11Botan is released under the Simplified BSD License (see license.txt)
12"""
13
14import optparse # pylint: disable=deprecated-module
15import sys
16import subprocess
17
18def get_module_list(configure_py):
19    configure = subprocess.Popen([configure_py, '--list-modules'], stdout=subprocess.PIPE)
20
21    (stdout, _) = configure.communicate()
22
23    if configure.returncode != 0:
24        raise Exception("Running configure.py --list-modules failed")
25
26    modules = [s.decode('ascii') for s in stdout.split()]
27    modules.remove('tpm') # can't test
28    modules.remove('base') # can't remove
29    return modules
30
31def get_concurrency():
32    def_concurrency = 2
33
34    try:
35        import multiprocessing
36        return max(def_concurrency, multiprocessing.cpu_count())
37    except ImportError:
38        return def_concurrency
39
40def try_to_run(cmdline):
41    print("Running %s ... " % (' '.join(cmdline)))
42    sys.stdout.flush()
43
44    cmd = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
45    (stdout, stderr) = cmd.communicate()
46
47    failed = (cmd.returncode != 0)
48
49    if failed:
50        print("FAILURE")
51        print(stdout)
52        print(stderr)
53        sys.stdout.flush()
54
55    return not failed
56
57def run_test_build(configure_py, modules, include, jobs, run_tests):
58    config = [configure_py, '--without-documentation']
59
60    if include:
61        config.append('--minimized')
62        if modules:
63            config.append('--enable-modules=' + ','.join(modules))
64    else:
65        config.append('--disable-modules=' + ','.join(modules))
66
67    if try_to_run(config) is False:
68        return False
69
70    if try_to_run(['make', '-j', str(jobs)]) is False:
71        return False
72
73    if run_tests is False:
74        return True
75
76    # Flaky test causing errors when running tests
77    tests_to_skip = []
78
79    cmdline = ['./botan-test', '--test-threads=%d' % (jobs)]
80
81    if len(tests_to_skip) > 0:
82        cmdline.append('--skip-tests=%s' % (','.join(tests_to_skip)))
83
84    return try_to_run(cmdline)
85
86def main(args):
87
88    # TODO take configure.py and botan-test paths via options
89
90    parser = optparse.OptionParser()
91
92    parser.add_option('--run-tests', default=False, action='store_true')
93    parser.add_option('--jobs', default=get_concurrency(),
94                      help="jobs to run (default %default)")
95
96    (options, args) = parser.parse_args(args)
97
98    run_tests = options.run_tests
99    jobs = int(options.jobs)
100
101    configure_py = './configure.py'
102    modules = get_module_list(configure_py)
103
104    cant_disable = ['block', 'hash', 'hex', 'mac', 'modes', 'rng', 'stream', 'utils', 'cpuid', 'entropy']
105    always_include = ['thread_utils', 'sha2_64']#, 'sha2_64', 'aes']
106
107    fails = 0
108    failed = []
109
110    for module in sorted(modules):
111        if (module in always_include) or (module in cant_disable):
112            continue # already testing it
113
114        extra = []
115        if module == 'auto_rng':
116            extra.append('dev_random')
117        if run_test_build(configure_py, [module] + always_include + extra, True, jobs, run_tests) is False:
118            failed.append(module)
119            fails += 1
120
121    for module in sorted(modules):
122        if module in cant_disable or module in always_include:
123            continue
124        if run_test_build(configure_py, [module], False, jobs, run_tests) is False:
125            failed.append(module)
126            fails += 1
127
128    if len(failed) > 0:
129        print("Failed building with %s" % (' '.join(failed)))
130    else:
131        print("All configurations ok")
132
133    return fails
134
135if __name__ == '__main__':
136    sys.exit(main(sys.argv))
137