Completed
Push — master ( 572ceb...f7a131 )
by srz
01:24
created

make_include_filename()   A

Complexity

Conditions 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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