Completed
Push — master ( 585429...816db8 )
by srz
01:13
created

add_files()   A

Complexity

Conditions 2

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 2
1
#!/usr/bin/env python
2
#
3
# iuwandbox.py
4
#
5
# Copyright (C) 2014-2016, 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
15
from time import sleep
16
from argparse import ArgumentParser
17
from wandbox import Wandbox
18
from requests.exceptions import HTTPError
19
20
IUTEST_FUSED_SRC = os.path.join(os.path.dirname(__file__), '../../fused-src/iutest.min.hpp')
21
IUTEST_INCLUDE_REGEX = re.compile(r'^\s*#\s*include\s*".*iutest\.hpp"')
22
EXPAND_INCLUDE_REGEX = re.compile(r'^\s*#\s*include\s*"(.*?)"')
23
24
25
#
26
# command line option
27
def parse_command_line():
28
    parser = ArgumentParser()
29
    parser.add_argument(
30
        '-v',
31
        '--version',
32
        action='version',
33
        version=u'%(prog)s version 4.2'
34
    )
35
    parser.add_argument(
36
        '--list_compiler',
37
        action='store_true',
38
        help='listup compiler.'
39
    )
40
    parser.add_argument(
41
        '--list_options',
42
        metavar='COMPILER',
43
        help='listup compiler options.'
44
    )
45
    parser.add_argument(
46
        '-c',
47
        '--compiler',
48
        default='gcc-head',
49
        help='compiler select. default: %(default)s'
50
    )
51
    parser.add_argument(
52
        '-x',
53
        '--options',
54
        help='used options for a compiler.'
55
    )
56
    parser.add_argument(
57
        '--default',
58
        action='store_true',
59
        help='use default options.'
60
    )
61
    parser.add_argument(
62
        '--boost',
63
        metavar='VERSION',
64
        help='set boost options version X.XX or nothing.'
65
    )
66
    parser.add_argument(
67
        '--sprout',
68
        action='store_true',
69
        help='use sprout.'
70
    )
71
    parser.add_argument(
72
        '--msgpack',
73
        action='store_true',
74
        help='use msgpack.'
75
    )
76
    parser.add_argument(
77
        '--stdin',
78
        help='set stdin.'
79
    )
80
    parser.add_argument(
81
        '-f',
82
        '--compiler_option_raw',
83
        metavar='OPTIONS',
84
        action='append',
85
        default=['-D__WANDBOX__'],
86
        help='compile-time any additional options.'
87
    )
88
    parser.add_argument(
89
        '-r',
90
        '--runtime_option_raw',
91
        metavar='OPTIONS',
92
        action='append',
93
        help='runtime-time any additional options.'
94
    )
95
    parser.add_argument(
96
        '-s',
97
        '--save',
98
        action='store_true',
99
        help='generate permanent link.'
100
    )
101
    parser.add_argument(
102
        '--permlink',
103
        metavar='ID',
104
        help='get permanent link.'
105
    )
106
    parser.add_argument(
107
        '-o',
108
        '--output',
109
        metavar='FILE',
110
        help='output source code.'
111
    )
112
    parser.add_argument(
113
        '--xml',
114
        metavar='FILE',
115
        help='output result xml.'
116
    )
117
    parser.add_argument(
118
        '--junit',
119
        metavar='FILE',
120
        help='output result junit xml.'
121
    )
122
    parser.add_argument(
123
        '--stderr',
124
        action='store_true',
125
        help='output stderr.'
126
    )
127
    parser.add_argument(
128
        '--encoding',
129
        help='set encoding.'
130
    )
131
    parser.add_argument(
132
        '--expand_include',
133
        action='store_true',
134
        help='expand include file.'
135
    )
136
    parser.add_argument(
137
        '--check_config',
138
        action='store_true',
139
        help='check config.'
140
    )
141
    parser.add_argument(
142
        '--verbose',
143
        action='store_true',
144
        help='verbose.'
145
    )
146
    parser.add_argument(
147
        '--dryrun',
148
        action='store_true',
149
        help='dryrun.'
150
    )
151
    parser.add_argument(
152
        'code',
153
        metavar='CODE',
154
        nargs='+',
155
        help='source code file'
156
    )
157
    options = parser.parse_args()
158
    return options, parser
159
160
161
# file open
162
def file_open(path, mode, encoding):
163
    if encoding:
164
        file = codecs.open(path, mode, encoding)
165
    else:
166
        file = open(path, mode)
167
    return file
168
169
170
# make include filename
171
def make_include_filename(path, includes, included_files):
172
    if path in included_files:
173
        return included_files[path]
174
    else:
175
        include_dir, include_filename = os.path.split(path)
176
        while include_filename in includes:
177
            include_dir, dirname = os.path.split(include_dir)
178
            include_filename = dirname + '__' + include_filename
179
        included_files[path] = include_filename
180
        return include_filename
181
182
183
# make code
184
def make_code(path, encoding, expand, includes, included_files):
185
    code = ''
186
    file = file_open(path, 'r', encoding)
187
    for line in file:
188
        m = IUTEST_INCLUDE_REGEX.match(line)
189
        if m:
190
            code += '#include "iutest.hpp"\n'
191
            code += '//origin>> ' + line
192
            if 'iutest.hpp' not in includes:
193
                try:
194
                    f = codecs.open(IUTEST_FUSED_SRC, 'r', 'utf-8-sig')
195
                    includes['iutest.hpp'] = f.read()
196
                except:
197
                    print('{0} is not found...'.format(IUTEST_FUSED_SRC))
198
                    print('please try \"make fused\"')
199
                    exit(1)
200
        else:
201
            m = EXPAND_INCLUDE_REGEX.match(line)
202
            if m:
203
                include_path = os.path.join(os.path.dirname(path), m.group(1))
204
                if os.path.exists(include_path):
205
                    expand_include_file_code = make_code(
206
                        include_path, encoding, expand, includes, included_files)
207
                    if expand:
208
                        code += expand_include_file_code
209
                        code += '//origin>> '
210
                    else:
211
                        include_abspath = os.path.abspath(include_path)
212
                        include_filename = make_include_filename(
213
                            include_abspath, includes, included_files)
214
                        if not include_filename == include_path:
215
                            code += '#include "' + include_filename + '"\n'
216
                            code += '//origin>> '
217
                        if include_filename not in includes:
218
                            includes[include_filename] = expand_include_file_code
219
            code += line
220
    file.close()
221
    return code
222
223
224
# check config
225
def check_config(options):
226
    if not find_compiler(options.compiler):
227
        print('Wandbox is not supported compiler [' + options.compiler + ']')
228
        listup_compiler()
229
        sys.exit(1)
230
231
232
# setup additional files
233
def add_files(w, fileinfos):
234
    for filename, code in fileinfos.items():
235
        w.add_file(filename, code)
236
237
238
# run wandbox
239
def run_wandbox(code, includes, impliments, options):
240
    w = Wandbox()
241
    w.compiler(options.compiler)
242
    opt = []
243
    if options.options:
244
        opt = options.options.split(',')
245
    elif options.default:
246
        opt = get_default_options(options.compiler)
247
    if options.boost:
248
        opt = list(filter(lambda s: s.find('boost') == -1, opt))
249
        opt.append('boost-' + str(options.boost))
250
    if options.sprout and 'sprout' not in opt:
251
        opt.append('sprout')
252
    if options.msgpack and 'msgpack' not in opt:
253
        opt.append('msgpack')
254
    w.options(','.join(opt))
255
    if options.stdin:
256
        w.stdin(options.stdin)
257
    if options.compiler_option_raw:
258
        co = '\n'.join(options.compiler_option_raw)
259
        co = co.replace('\\n', '\n')
260
        if options.compiler == "clang-3.5":
261
            co += "\n-DIUTEST_HAS_HDR_CXXABI=0"
262
        if options.compiler in ["clang-3.2", "clang-3.1", "clang-3.0"]:
263
            co += "\n-Qunused-arguments"
264
        w.compiler_options(co)
265
    if options.runtime_option_raw:
266
        ro = ''
267
        for opt in options.runtime_option_raw:
268
            ro += opt + '\n'
269
        ro = ro.replace('\\n', '\n')
270
        w.runtime_options(ro)
271
    if options.save:
272
        w.permanent_link(options.save)
273
    for filename in impliments.keys():
274
        w.add_compiler_options(filename)
275
    if options.verbose:
276
        w.dump()
277
    w.code(code)
278
    add_files(w, impliments)
279
    add_files(w, includes)
280
    if options.dryrun:
281
        sys.exit(0)
282
283
    def run(retries):
284
        try:
285
            return w.run()
286
        except HTTPError as e:
287
            if e.response.status_code == 504 and retries > 0:
288
                print(e.message)
289
                print("wait 30sec...")
290
                sleep(30)
291
                return run(retries - 1)
292
            else:
293
                raise
294
        except:
295
            raise
296
    return run(3)
297
298
299
# show result
300
def show_result(r, options):
301
    if 'error' in r:
302
        print(r['error'])
303
        sys.exit(1)
304
    if options.stderr:
305
        if 'compiler_output' in r:
306
            print('compiler_output:')
307
            print(r['compiler_output'].encode('utf_8'))
308
        if 'compiler_error' in r:
309
            sys.stderr.write(r['compiler_error'].encode('utf_8'))
310
        if 'program_output' in r:
311
            print('program_output:')
312
            print(r['program_output'].encode('utf_8'))
313
        if options.xml is None and options.junit is None and 'program_error' in r:
314
            sys.stderr.write(r['program_error'].encode('utf_8'))
315
    else:
316
        if 'compiler_message' in r:
317
            print('compiler_message:')
318
            print(r['compiler_message'].encode('utf_8'))
319
        if 'program_message' in r:
320
            print('program_message:')
321
            print(r['program_message'].encode('utf_8'))
322
    if 'url' in r:
323
        print('permlink: ' + r['permlink'])
324
        print('url: ' + r['url'])
325
    if 'signal' in r:
326
        print('signal: ' + r['signal'])
327
    if 'status' in r:
328
        return int(r['status'])
329
    return 1
330
331
332
# show parameter
333
def show_parameter(r):
334
    if 'compiler' in r:
335
        print('compiler:' + r['compiler'])
336
    if 'options' in r:
337
        print('options:' + r['options'])
338
    if 'compiler-option-raw' in r:
339
        print('compiler-option-raw:' + r['compiler-option-raw'])
340
    if 'runtime-option-raw' in r:
341
        print('runtime-option-raw' + r['runtime-option-raw'])
342
    if 'created-at' in r:
343
        print(r['created-at'])
344
345
346
def set_output_xml(options, t, xml):
347
    options.stderr = True
348
    if options.runtime_option_raw:
349
        options.runtime_option_raw.append("--iutest_output=" + t + ":" + xml)
350
    else:
351
        options.runtime_option_raw = ["--iutest_output=" + t + ":" + xml]
352
353
354
def run(options):
355
    main_filepath = options.code[0].strip()
356
    if not os.path.exists(main_filepath):
357
        sys.exit(1)
358
    includes = {}
359
    included_files = {}
360
    impliments = {}
361
    code = make_code(main_filepath, options.encoding, options.expand_include, includes, included_files)
362
    
363
    for filepath_ in options.code[1:]:
364
        filepath = filepath_.strip()
365
        impliments[os.path.basename(filepath)] = make_code(filepath, options.encoding, options.expand_include, includes, included_files)
366
367
    if options.output:
368
        f = file_open(options.output, 'w', options.encoding)
369
        f.write(code)
370
        f.close()
371
    xml = None
372
    if options.xml:
373
        xml = options.xml
374
        set_output_xml(options, 'xml', xml)
375
    if options.junit:
376
        xml = options.junit
377
        set_output_xml(options, 'junit', xml)
378
    r = run_wandbox(code, includes, impliments, options)
379
    b = show_result(r, options)
380
    if xml and 'program_error' in r:
381
        f = file_open(xml, 'w', options.encoding)
382
        f.write(r['program_error'])
383
        f.close()
384
    sys.exit(b)
385
386
387
# listup compiler
388
def listup_compiler(verbose):
389
    w = Wandbox()
390
    r = w.get_compiler_list()
391
    for d in r:
392
        if d['language'] == 'C++':
393
            if verbose:
394
                print(d['name'] + ' (' + d['version'] + ')')
395
            else:
396
                print(d['name'])
397
398
399
# find compiler
400
def find_compiler(c):
401
    w = Wandbox()
402
    r = w.get_compiler_list()
403
    for d in r:
404
        if d['language'] == 'C++' and d['name'] == c:
405
            return True
406
    return False
407
408
409
# listup options
410
def listup_options(compiler):
411
    w = Wandbox()
412
    r = w.get_compiler_list()
413
    for d in r:
414
        if d['name'] == compiler:
415
            if 'switches' in d:
416
                switches = d['switches']
417
                for s in switches:
418
                    if 'name' in s:
419
                        if s['default']:
420
                            print(s['name'] + ' (default)')
421
                        else:
422
                            print(s['name'])
423
                    elif 'options' in s:
424
                        print(s['default'] + ' (default)')
425
                        for o in s['options']:
426
                            print('  ' + o['name'])
427
428
429
# get default options
430
def get_default_options(compiler):
431
    w = Wandbox()
432
    r = w.get_compiler_list()
433
    opt = []
434
    for d in r:
435
        if d['name'] == compiler:
436
            if 'switches' in d:
437
                switches = d['switches']
438
                for s in switches:
439
                    if 'name' in s:
440
                        if s['default']:
441
                            opt.append(s['name'])
442
                    elif 'options' in s:
443
                        opt.append(s['default'])
444
    return opt
445
446
447
# get permlink
448
def get_permlink(options):
449
    w = Wandbox()
450
    r = w.get_permlink(options.permlink)
451
    p = r['parameter']
452
    show_parameter(p)
453
    print('result:')
454
    b = show_result(r['result'], options)
455
    if options.output:
456
        f = open(options.output, 'w')
457
        f.write(p['code'])
458
        f.close()
459
    sys.exit(b)
460
461
462
def main():
463
    options, parser = parse_command_line()
464
    if options.list_compiler:
465
        listup_compiler(options.verbose)
466
    elif options.list_options:
467
        listup_options(options.list_options)
468
    elif options.permlink:
469
        get_permlink(options)
470
    else:
471
        if options.check_config:
472
            check_config(options)
473
        elif options.code is None:
474
            parser.print_help()
475
            sys.exit(1)
476
        run(options)
477
478
if __name__ == '__main__':
479
    main()
480