Test Failed
Push — master ( 536f52...2dce7e )
by srz
01:23
created

expand_wandbox_options()   C

Complexity

Conditions 13

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
rs 5.2936
cc 13

How to fix   Complexity   

Complexity

Complex classes like expand_wandbox_options() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python
2
#
3
# iuwandbox.py
4
#
5
# Copyright (C) 2014-2017, Takazumi Shirayanagi
6
# This software is released under the new BSD License,
7
# see LICENSE
8
#
9
10
import os
11
import sys
12
import re
13
import codecs
14
import argparse
15
16
from time import sleep
17
from argparse import ArgumentParser
18
from wandbox import Wandbox
19
from requests.exceptions import HTTPError
20
21
IUTEST_FUSED_SRC = os.path.normpath(os.path.join(os.path.dirname(__file__), '../../fused-src/iutest.min.hpp'))
22
IUTEST_INCLUDE_PATH = os.path.normpath(os.path.join(os.path.dirname(__file__), '../../include'))
23
IUTEST_INCLUDE_REGEX = re.compile(r'^\s*#\s*include\s*".*(iutest|iutest_switch)\.hpp"')
24
EXPAND_INCLUDE_REGEX = re.compile(r'^\s*#\s*include\s*"(.*?)"')
25
IUTEST_INCG_REGEX = re.compile(r'\s*#\s*define[/\s]*(INCG_IRIS_\S*)\s*')
26
27
iutest_incg_list = []
28
workaround = True
29
30
#
31
# command line option
32
def parse_command_line():
33
    parser = ArgumentParser()
34
    parser.add_argument(
35
        '-v',
36
        '--version',
37
        action='version',
38
        version=u'%(prog)s version 5.2'
39
    )
40
    parser.add_argument(
41
        '--list_compiler',
42
        '--list-compiler',
43
        action='store_true',
44
        help='listup compiler.'
45
    )
46
    parser.add_argument(
47
        '--list_options',
48
        '--list-options',
49
        metavar='COMPILER',
50
        help='listup compiler options.'
51
    )
52
    parser.add_argument(
53
        '-c',
54
        '--compiler',
55
        default='gcc-head',
56
        help='compiler select. default: %(default)s'
57
    )
58
    parser.add_argument(
59
        '-x',
60
        '--options',
61
        help='used options for a compiler.'
62
    )
63
    parser.add_argument(
64
        '--default',
65
        action='store_true',
66
        help='it is not work. default options are set by default (deprecated)'
67
    )
68
    parser.add_argument(
69
        '--no-default',
70
        action='store_true',
71
        help='no set default options.'
72
    )
73
    parser.add_argument(
74
        '--std',
75
        metavar='VERSION',
76
        help='set --std options.'
77
    )
78
    parser.add_argument(
79
        '--boost',
80
        metavar='VERSION',
81
        help='set boost options version X.XX or nothing.'
82
    )
83
    parser.add_argument(
84
        '--optimize',
85
        action='store_true',
86
        help='use optimization.'
87
    )
88
    parser.add_argument(
89
        '--cpp-verbose',
90
        action='store_true',
91
        help='use cpp-verbose.'
92
    )
93
    parser.add_argument(
94
        '--sprout',
95
        action='store_true',
96
        help='use sprout.'
97
    )
98
    parser.add_argument(
99
        '--msgpack',
100
        action='store_true',
101
        help='use msgpack.'
102
    )
103
    parser.add_argument(
104
        '--stdin',
105
        help='set stdin.'
106
    )
107
    parser.add_argument(
108
        '-f',
109
        '--compiler_option_raw',
110
        '--compiler-option-raw',
111
        metavar='OPTIONS',
112
        action='append',
113
        default=['-D__WANDBOX__'],
114
        help='compile-time any additional options.'
115
    )
116
    parser.add_argument(
117
        '-r',
118
        '--runtime_option_raw',
119
        '--runtime-option-raw',
120
        metavar='OPTIONS',
121
        action='append',
122
        help='runtime-time any additional options.'
123
    )
124
    parser.add_argument(
125
        '-s',
126
        '--save',
127
        action='store_true',
128
        help='generate permanent link.'
129
    )
130
    parser.add_argument(
131
        '--permlink',
132
        metavar='ID',
133
        help='get permanent link.'
134
    )
135
    parser.add_argument(
136
        '-o',
137
        '--output',
138
        metavar='FILE',
139
        help='output source code.'
140
    )
141
    parser.add_argument(
142
        '--xml',
143
        metavar='FILE',
144
        help='output result xml.'
145
    )
146
    parser.add_argument(
147
        '--junit',
148
        metavar='FILE',
149
        help='output result junit xml.'
150
    )
151
    parser.add_argument(
152
        '--stderr',
153
        action='store_true',
154
        help='output stderr.'
155
    )
156
    parser.add_argument(
157
        '--encoding',
158
        help='set encoding.'
159
    )
160
    parser.add_argument(
161
        '--expand_include',
162
        '--expand-include',
163
        action='store_true',
164
        help='expand include file.'
165
    )
166
    parser.add_argument(
167
        '--make',
168
        action='store_true',
169
        help=argparse.SUPPRESS
170
    )
171
    parser.add_argument(
172
        '--check_config',
173
        '--check-config',
174
        action='store_true',
175
        help='check config.'
176
    )
177
    parser.add_argument(
178
        '--verbose',
179
        action='store_true',
180
        help='verbose.'
181
    )
182
    parser.add_argument(
183
        '--dryrun',
184
        action='store_true',
185
        help='dryrun.'
186
    )
187
    parser.add_argument(
188
        'code',
189
        metavar='CODE',
190
        nargs='*',
191
        help='source code file'
192
    )
193
    options = parser.parse_args()
194
    return options, parser
195
196
197
# file open
198
def file_open(path, mode, encoding):
199
    if encoding:
200
        file = codecs.open(path, mode, encoding)
201
    else:
202
        file = open(path, mode)
203
    return file
204
205
206
# make include filename
207
def make_include_filename(path, includes, included_files):
208
    if path in included_files:
209
        return included_files[path]
210
    else:
211
        include_dir, include_filename = os.path.split(path)
212
        while include_filename in includes:
213
            include_dir, dirname = os.path.split(include_dir)
214
            include_filename = dirname + '__' + include_filename
215
        included_files[path] = include_filename
216
        return include_filename
217
218
219
# 
220
def is_iutest_included_file(filepath):
221
    if os.path.abspath(filepath).startswith(IUTEST_INCLUDE_PATH):
222
        incg = 'INCG_IRIS_' + os.path.basename(filepath).upper().replace('.', '_')
223
        for included_incg in iutest_incg_list:
224
            if included_incg.startswith(incg):
225
                return True
226
    return False
227
228
229
# make code
230
def make_code(path, encoding, expand, includes, included_files):
231
    code = ''
232
    file = file_open(path, 'r', encoding)
233
    for line in file:
234
        m = IUTEST_INCLUDE_REGEX.match(line)
235
        if m:
236
            code += '#include "iutest.hpp"\n'
237
            code += '//origin>> ' + line
238
            if 'iutest.hpp' not in includes:
239
                try:
240
                    f = codecs.open(IUTEST_FUSED_SRC, 'r', 'utf-8-sig')
241
                    iutest_src = f.read()
242
                    f.close()
243
                    includes['iutest.hpp'] = iutest_src
244
                    global iutest_incg_list
245
                    iutest_incg_list = IUTEST_INCG_REGEX.findall(iutest_src)
246
                except:
247
                    print('{0} is not found...'.format(IUTEST_FUSED_SRC))
248
                    print('please try \"make fused\"')
249
                    exit(1)
250
        else:
251
            m = EXPAND_INCLUDE_REGEX.match(line)
252
            if m:
253
                include_path = os.path.normpath(os.path.join(os.path.dirname(path), m.group(1)))
254
                if is_iutest_included_file(include_path):
255
                    code += '//origin>> '
256
                elif os.path.exists(include_path):
257
                    if expand:
258
                        expand_include_file_code = make_code(
259
                            include_path, encoding, expand, includes, included_files)
260
                        code += expand_include_file_code
261
                        code += '//origin>> '
262
                    else:
263
                        include_abspath = os.path.abspath(include_path)
264
                        include_filename = make_include_filename(
265
                            include_abspath, includes, included_files)
266
                        if not include_filename == include_path:
267
                            code += '#include "' + include_filename + '"\n'
268
                            code += '//origin>> '
269
                        if include_filename not in includes:
270
                            includes[include_filename] = ''
271
                            expand_include_file_code = make_code(
272
                                include_path, encoding, expand, includes, included_files)
273
                            includes[include_filename] = expand_include_file_code
274
            code += line
275
    file.close()
276
    return code
277
278
279
# check config
280
def check_config(options):
281
    has_error = False
282
    if not find_compiler(options.compiler):
283
        print('Wandbox is not supported compiler [' + options.compiler + ']')
284
        listup_compiler()
285
        has_error = True
286
    if options.options or options.std:
287
        opt = get_options(options.compiler)
288
        if options.options:
289
            for o in options.options.split(','):
290
                if o not in opt:
291
                    print('Wandbox is not supported option [{0}] ({1})'.format(o, options.compiler))
292
                    has_error = True
293
        if options.std:
294
            if options.std not in opt:
295
                print('Wandbox is not supported option [{0}] ({1})'.format(options.std, options.compiler))
296
                has_error = True
297
        if has_error:
298
            listup_options(options.compiler)
299
    if has_error:
300
        sys.exit(1)
301
    if options.default:
302
        print('--default option is not work. default options are set by default (deprecated)')
303
304
305
# setup additional files
306
def add_files(w, fileinfos):
307
    for filename, code in fileinfos.items():
308
        w.add_file(filename, code)
309
310
311
# create opt list
312
def create_option_list(options):
313
    def filterout_cppver(opt):
314
        tmp = list(filter(lambda s: s.find('c++') == -1, opt))
315
        tmp = list(filter(lambda s: s.find('gnu++') == -1, tmp))
316
        return tmp
317
    opt = []
318
    if not options.no_default:
319
        opt = get_default_options(options.compiler)
320
    if options.options:
321
        for o in options.options.split(','):
322
            if o not in opt:
323
                if (o.find('c++') == 0) or (o.find('gnu++') == 0):
324
                    opt = filterout_cppver(opt)
325
                opt.append(o)
326
    # std
327
    if options.std:
328
        opt = filterout_cppver(opt)
329
        opt.append(options.std)
330
    # optimize
331
    if options.optimize and ('optimize' not in opt):
332
        opt.append('optimize')
333
    # cpp-verbose
334
    if options.cpp_verbose and ('cpp-verbose' not in opt):
335
        opt.append('cpp-verbose')
336
    # boost
337
    if workaround:
338
        pass
339
#        if options.compiler in ['clang-3.4', 'clang-3.3']:
340
#            if not options.boost:
341
#                options.boost = 'nothing'
342
    if options.boost:
343
        if options.compiler not in options.boost:
344
            options.boost = options.boost + '-' + options.compiler
345
        opt = list(filter(lambda s: s.find('boost') == -1, opt))
346
        opt.append('boost-' + str(options.boost))
347
    # sprout
348
    if options.sprout and ('sprout' not in opt):
349
        opt.append('sprout')
350
    # msgpack
351
    if options.msgpack and ('msgpack' not in opt):
352
        opt.append('msgpack')
353
    return opt
354
355
356
def expand_wandbox_options(w, compiler, options):
357
    colist = []
358
    defs = {}
359
    for d in w.get_compiler_list():
360
        if d['name'] == compiler:
361
            if 'switches' in d:
362
                switches = d['switches']
363
                for s in switches:
364
                    if ('name' in s) and ('display-flags' in s):
365
                        defs[s['name']] = s['display-flags']
366
                    elif 'options' in s:
367
                        for o in s['options']:
368
                            if ('name' in o) and ('display-flags' in o):
369
                                defs[o['name']] = o['display-flags']
370
    for opt in options:
371
        if opt in defs:
372
            colist.extend(defs[opt].split())
373
    return colist
374
375
376
def run_wandbox_impl(w, options, retries):
377
    if options.dryrun:
378
        sys.exit(0)
379
    def run(retries):
380
        try:
381
            return w.run()
382
        except HTTPError as e:
383
            if e.response.status_code == 504 and retries > 0:
384
                try:
385
                    print(e.message)
386
                except:
387
                    pass
388
                print('wait 30sec...')
389
                sleep(30)
390
                return run(retries - 1)
391
            else:
392
                raise
393
        except:
394
            raise
395
    return run(retries)
396
397
398
def create_compiler_raw_option_list(options):
399
    colist = []
400
    if options.compiler_option_raw:
401
        raw_options = options.compiler_option_raw
402
        for x in raw_options:
403
            colist.extend(re.split('\s(?=-)', x.strip('"')))
404
    return colist
405
406
407
# run wandbox (makefile)
408
def run_wandbox_make(main_filepath, code, includes, impliments, options):
409
    w = Wandbox()
410
    w.compiler('bash')
411
    woptions = create_option_list(options)
412
    if options.stdin:
413
        w.stdin(options.stdin)
414
    impliments[os.path.basename(main_filepath)] = code
415
416
    colist = create_compiler_raw_option_list(options)
417
    colist.extend(expand_wandbox_options(w, options.compiler, woptions))
418
419
    rolist = []
420
    if options.runtime_option_raw:
421
        for opt in options.runtime_option_raw:
422
            rolist.extend(opt.split())
423
424
    makefile = '#!/bin/make\n# generate makefile by iuwandbox.py\n'
425
    makefile += '\nCXXFLAGS+='
426
    for opt in colist:
427
        makefile += opt + ' '
428
    makefile += '\nOBJS='
429
    for filename in impliments.keys():
430
        makefile += os.path.splitext(filename)[0] + '.o '
431
432
    makefile += '\n\
433
prog: $(OBJS)\n\
434
\t$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS)\n\
435
'
436
437
    impliments['Makefile'] = makefile
438
439
    bashscript = 'make -j 4\n'
440
    bashscript += './prog '
441
    for opt in rolist:
442
        bashscript += opt + ' '
443
    bashscript += '\n'
444
    w.code(bashscript)
445
    
446
    if options.save:
447
        w.permanent_link(options.save)
448
    if options.verbose:
449
        w.dump()
450
    add_files(w, impliments)
451
    add_files(w, includes)
452
453
    return run_wandbox_impl(w, options, 3)
454
455
456
# run wandbox (cxx)
457
def run_wandbox_cxx(code, includes, impliments, options):
458
    w = Wandbox()
459
    w.compiler(options.compiler)
460
    w.options(','.join(create_option_list(options)))
461
    if options.stdin:
462
        w.stdin(options.stdin)
463
    colist = create_compiler_raw_option_list(options)
464
465
    if workaround:
466
        if options.compiler in ['clang-3.2']:
467
            colist.append('-ftemplate-depth=1024')
468
#        if options.compiler in ['clang-3.4']:
469
#            colist.append('-DIUTEST_HAS_HDR_CXXABI=0')
470
#        if options.compiler in ['clang-3.3', 'clang-3.2', 'clang-3.1', 'clang-3.0']:
471
#            colist.append('-Qunused-arguments')
472
#        if options.compiler in ['clang-3.4', 'clang-3.3']:
473
#            colist.append('-fno-exceptions')
474
#            colist.append('-fno-rtti')
475
        pass
476
    if len(colist) > 0:
477
        co = '\n'.join(colist)
478
        co = co.replace('\\n', '\n')
479
        w.compiler_options(co)
480
    if options.runtime_option_raw:
481
        rolist = []
482
        for opt in options.runtime_option_raw:
483
            rolist.extend(opt.split())
484
        ro = '\n'.join(rolist)
485
        ro = ro.replace('\\n', '\n')
486
        w.runtime_options(ro)
487
    if options.save:
488
        w.permanent_link(options.save)
489
    for filename in impliments.keys():
490
        w.add_compiler_options(filename)
491
    if options.verbose:
492
        w.dump()
493
    w.code(code)
494
    add_files(w, impliments)
495
    add_files(w, includes)
496
497
    return run_wandbox_impl(w, options, 3)
498
499
500
# run wandbox
501
def run_wandbox(main_filepath, code, includes, impliments, options):
502
    if options.make:
503
        return run_wandbox_make(main_filepath, code, includes, impliments, options)
504
    else:
505
        return run_wandbox_cxx(code, includes, impliments, options)
506
507
508
def wandbox_hint(r):
509
    if 'compiler_error' in r:
510
        if 'undefined reference to `init_unit_test_suite' in r['compiler_error']:
511
            print('hint:')
512 View Code Duplication
            print('  If you do not use boost test, please specify the file with the main function first.')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
513
514
515
# show result
516
def show_result(r, options):
517
    if 'error' in r:
518
        print(r['error'])
519
        sys.exit(1)
520
    if options.stderr:
521
        if 'compiler_output' in r:
522
            print('compiler_output:')
523
            print(r['compiler_output'].encode('utf_8'))
524
        if 'compiler_error' in r:
525
            sys.stderr.write(r['compiler_error'].encode('utf_8'))
526
        if 'program_output' in r:
527
            print('program_output:')
528
            print(r['program_output'].encode('utf_8'))
529
        if options.xml is None and options.junit is None and 'program_error' in r:
530
            sys.stderr.write(r['program_error'].encode('utf_8'))
531 View Code Duplication
    else:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
532
        if 'compiler_message' in r:
533
            print('compiler_message:')
534
            print(r['compiler_message'].encode('utf_8'))
535
        if 'program_message' in r:
536
            print('program_message:')
537
            print(r['program_message'].encode('utf_8'))
538
    if 'url' in r:
539
        print('permlink: ' + r['permlink'])
540
        print('url: ' + r['url'])
541
    if 'signal' in r:
542
        print('signal: ' + r['signal'])
543
    wandbox_hint(r)
544
545
    if 'status' in r:
546
        return int(r['status'])
547
    return 1
548
549 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
550
# show parameter
551
def show_parameter(r):
552
    if 'compiler' in r:
553
        print('compiler:' + r['compiler'])
554
    if 'options' in r:
555
        print('options:' + r['options'])
556
    if 'compiler-option-raw' in r:
557
        print('compiler-option-raw:' + r['compiler-option-raw'])
558
    if 'runtime-option-raw' in r:
559
        print('runtime-option-raw' + r['runtime-option-raw'])
560
    if 'created-at' in r:
561
        print(r['created-at'])
562
563
564
def set_output_xml(options, t, xml):
565
    options.stderr = True
566
    if options.runtime_option_raw:
567
        options.runtime_option_raw.append('--iutest_output=' + t + ':' + xml)
568 View Code Duplication
    else:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
569
        options.runtime_option_raw = ['--iutest_output=' + t + ':' + xml]
570
571
572
def run(options):
573
    main_filepath = options.code[0].strip()
574
    if not os.path.exists(main_filepath):
575
        sys.exit(1)
576
    includes = {}
577
    included_files = {}
578
    impliments = {}
579
    code = make_code(main_filepath, options.encoding, options.expand_include, includes, included_files)
580
    
581
    for filepath_ in options.code[1:]:
582
        filepath = filepath_.strip()
583
        impliments[os.path.basename(filepath)] = make_code(filepath, options.encoding, options.expand_include, includes, included_files)
584
585
    if options.output:
586
        f = file_open(options.output, 'w', options.encoding)
587
        f.write(code)
588
        f.close()
589
    xml = None
590
    if options.xml:
591
        xml = options.xml
592
        set_output_xml(options, 'xml', xml)
593
    if options.junit:
594
        xml = options.junit
595
        set_output_xml(options, 'junit', xml)
596
    r = run_wandbox(main_filepath, code, includes, impliments, options)
597
    b = show_result(r, options)
598
    if xml and 'program_error' in r:
599
        f = file_open(xml, 'w', options.encoding)
600
        f.write(r['program_error'])
601
        f.close()
602
    sys.exit(b)
603
604
605
# listup compiler
606
def listup_compiler(verbose):
607
    w = Wandbox()
608
    r = w.get_compiler_list()
609
    for d in r:
610
        if d['language'] == 'C++':
611
            if verbose:
612
                print(d['name'] + ' (' + d['version'] + ')')
613
            else:
614
                print(d['name'])
615
616
617
# find compiler
618
def find_compiler(c):
619
    w = Wandbox()
620
    r = w.get_compiler_list()
621
    for d in r:
622
        if d['language'] == 'C++' and d['name'] == c:
623
            return True
624
    return False
625
626
627
# listup options
628
def listup_options(compiler):
629
    w = Wandbox()
630
    r = w.get_compiler_list()
631
    for d in r:
632
        if d['name'] == compiler:
633
            print('# ' + compiler)
634
            if 'switches' in d:
635
                switches = d['switches']
636
                for s in switches:
637
                    if 'name' in s:
638
                        if s['default']:
639
                            print(s['name'] + ' (default)')
640
                        else:
641
                            print(s['name'])
642
                    elif 'options' in s:
643
                        print(s['default'] + ' (default)')
644
                        for o in s['options']:
645
                            print('  ' + o['name'])
646
647
648
def get_options(compiler):
649
    w = Wandbox()
650
    r = w.get_compiler_list()
651
    opt = []
652
    for d in r:
653 View Code Duplication
        if d['name'] == compiler:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
654
            if 'switches' in d:
655
                switches = d['switches']
656
                for s in switches:
657
                    if 'name' in s:
658
                        opt.append(s['name'])
659
                    elif 'options' in s:
660
                        opt.append(s['default'])
661
                        for o in s['options']:
662
                            opt.append(o['name'])
663
    return opt
664
665
666
# get default options
667
def get_default_options(compiler):
668
    w = Wandbox()
669
    r = w.get_compiler_list()
670
    opt = []
671
    for d in r:
672 View Code Duplication
        if d['name'] == compiler:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
673
            if 'switches' in d:
674
                switches = d['switches']
675
                for s in switches:
676
                    if 'name' in s:
677
                        if s['default']:
678
                            opt.append(s['name'])
679
                    elif 'options' in s:
680
                        opt.append(s['default'])
681
    return opt
682
683
684
# get permlink
685
def get_permlink(options):
686
    w = Wandbox()
687
    r = w.get_permlink(options.permlink)
688
    p = r['parameter']
689
    show_parameter(p)
690
    print('result:')
691
    b = show_result(r['result'], options)
692
    if options.output:
693
        f = open(options.output, 'w')
694
        f.write(p['code'])
695
        f.close()
696
    sys.exit(b)
697
698
699
def main():
700
    options, parser = parse_command_line()
701
    if options.list_compiler:
702
        listup_compiler(options.verbose)
703
    elif options.list_options:
704
        listup_options(options.list_options)
705
    elif options.permlink:
706
        get_permlink(options)
707
    else:
708
        if options.check_config:
709
            check_config(options)
710
        elif len(options.code) == 0:
711
            parser.print_help()
712
            sys.exit(1)
713
        run(options)
714
715
if __name__ == '__main__':
716
    main()
717