]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.3.0/config/genconfig.py
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / config / genconfig.py
1 #!/usr/bin/python
2 #
3 # Process Duktape option metadata and produce various useful outputs:
4 #
5 # - duk_config.h matching Duktape 1.x feature option model (DUK_OPT_xxx)
6 # - duk_config.h for a selected platform, compiler, forced options, etc.
7 # - option documentation for Duktape 1.x feature options (DUK_OPT_xxx)
8 # - option documentation for Duktape 1.x/2.x config options (DUK_USE_xxx)
9 #
10 # Genconfig tries to build all outputs based on modular metadata, so that
11 # managing a large number of config options (which is hard to avoid given
12 # the wide range of targets Duktape supports) remains maintainable.
13 #
14 # Genconfig does *not* try to support all exotic platforms out there.
15 # Instead, the goal is to allow the metadata to be extended, or to provide
16 # a reasonable starting point for manual duk_config.h tweaking.
17 #
18 # NOTE: For Duktape 1.3 release the main goal is to autogenerate a Duktape
19 # 1.2 compatible "autodetect" header from snippets. Other outputs are still
20 # experimental.
21 #
22
23 import os
24 import sys
25 import re
26 import json
27 import yaml
28 import optparse
29 import tarfile
30 import tempfile
31 import atexit
32 import shutil
33 import StringIO
34
35 #
36 # Globals holding scanned metadata, helper snippets, etc
37 #
38
39 # Metadata to scan from config files.
40 use_defs = None
41 use_defs_list = None
42 opt_defs = None
43 opt_defs_list = None
44 use_tags = None
45 use_tags_list = None
46 tags_meta = None
47 required_use_meta_keys = [
48 'define',
49 'introduced',
50 'default',
51 'tags',
52 'description'
53 ]
54 allowed_use_meta_keys = [
55 'define',
56 'feature_enables',
57 'feature_disables',
58 'feature_snippet',
59 'related_feature_defines',
60 'introduced',
61 'deprecated',
62 'removed',
63 'unused',
64 'requires',
65 'conflicts',
66 'related',
67 'default',
68 'tags',
69 'description',
70 ]
71 required_opt_meta_keys = [
72 'define',
73 'introduced',
74 'tags',
75 'description'
76 ]
77 allowed_opt_meta_keys = [
78 'define',
79 'introduced',
80 'deprecated',
81 'removed',
82 'unused',
83 'requires',
84 'conflicts',
85 'related',
86 'tags',
87 'description'
88 ]
89
90 # Preferred tag order for option documentation.
91 doc_tag_order = [
92 'portability',
93 'memory',
94 'lowmemory',
95 'ecmascript',
96 'execution',
97 'debugger',
98 'debug',
99 'development'
100 ]
101
102 # Preferred tag order for generated C header files.
103 header_tag_order = doc_tag_order
104
105 # Helper headers snippets.
106 helper_snippets = None
107
108 # Assume these provides come from outside.
109 assumed_provides = {
110 'DUK_SINGLE_FILE': True, # compiling Duktape from a single source file (duktape.c) version
111 'DUK_COMPILING_DUKTAPE': True, # compiling Duktape (not user application)
112 'DUK_CONFIG_H_INCLUDED': True, # artifact, include guard
113 }
114
115 # Platform files must provide at least these (additional checks
116 # in validate_platform_file()).
117 platform_required_provides = [
118 'DUK_USE_OS_STRING',
119 'DUK_SETJMP', 'DUK_LONGJMP',
120 ]
121
122 # Architecture files must provide at least these (additional checks
123 # in validate_architecture_file()).
124 architecture_required_provides = [
125 'DUK_USE_ARCH_STRING',
126 'DUK_USE_ALIGN_BY', 'DUK_USE_UNALIGNED_ACCESSES_POSSIBLE', 'DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS',
127 'DUK_USE_PACKED_TVAL', 'DUK_USE_PACKED_TVAL_POSSIBLE'
128 ]
129
130 # Compiler files must provide at least these (additional checks
131 # in validate_compiler_file()).
132 compiler_required_provides = [
133 # XXX: incomplete, maybe a generic fill-in for missing stuff because
134 # there's quite a lot of required compiler defines.
135
136 'DUK_USE_COMPILER_STRING',
137
138 'DUK_EXTERNAL_DECL', 'DUK_EXTERNAL',
139 'DUK_INTERNAL_DECL', 'DUK_INTERNAL',
140 'DUK_LOCAL_DECL', 'DUK_LOCAL',
141
142 'DUK_FILE_MACRO', 'DUK_LINE_MACRO', 'DUK_FUNC_MACRO'
143 ]
144
145 #
146 # Miscellaneous helpers
147 #
148
149 def get_auto_delete_tempdir():
150 tmpdir = tempfile.mkdtemp(suffix='-genconfig')
151 def _f(dirname):
152 print 'Deleting temporary directory: %r' % dirname
153 if os.path.isdir(dirname) and '-genconfig' in dirname:
154 shutil.rmtree(dirname)
155 atexit.register(_f, tmpdir)
156 return tmpdir
157
158 def strip_comments_from_lines(lines):
159 # Not exact but close enough. Doesn't handle string literals etc,
160 # but these are not a concrete issue for scanning preprocessor
161 # #define references.
162 #
163 # Comment contents are stripped of any DUK_ prefixed text to avoid
164 # incorrect requires/provides detection. Other comment text is kept;
165 # in particular a "/* redefine */" comment must remain intact here.
166 #
167 # Avoid Python 2.6 vs. Python 2.7 argument differences.
168
169 def censor(x):
170 return re.sub(re.compile('DUK_\w+', re.MULTILINE), 'xxx', x.group(0))
171
172 tmp = '\n'.join(lines)
173 tmp = re.sub(re.compile('/\*.*?\*/', re.MULTILINE | re.DOTALL), censor, tmp)
174 tmp = re.sub(re.compile('//.*?$', re.MULTILINE), censor, tmp)
175 return tmp.split('\n')
176
177 # Header snippet representation: lines, provides defines, requires defines.
178 re_line_provides = re.compile(r'^#(?:define|undef)\s+(\w+).*$')
179 re_line_requires = re.compile(r'(DUK_[A-Z0-9_]+)') # uppercase only, don't match DUK_USE_xxx for example
180 class Snippet:
181 lines = None # lines of text and/or snippets
182 provides = None # map from define to 'True' for now
183 requires = None # map from define to 'True' for now
184
185 def __init__(self, lines, provides=None, requires=None, autoscan_requires=True, autoscan_provides=True):
186 self.lines = []
187 if not isinstance(lines, list):
188 raise Exception('Snippet constructor must be a list (not e.g. a string): %s' % repr(lines))
189 for line in lines:
190 if isinstance(line, str):
191 self.lines.append(line)
192 elif isinstance(line, unicode):
193 self.lines.append(line.encode('utf-8'))
194 else:
195 raise Exception('invalid line: %r' % line)
196 self.provides = {}
197 if provides is not None:
198 for k in provides.keys():
199 self.provides[k] = True
200 self.requires = {}
201 if requires is not None:
202 for k in requires.keys():
203 self.requires[k] = True
204
205 stripped_lines = strip_comments_from_lines(lines)
206 # for line in stripped_lines: print(line)
207
208 for line in stripped_lines:
209 # Careful with order, snippet may self-reference its own
210 # defines in which case there's no outward dependency.
211 # (This is not 100% because the order of require/provide
212 # matters and this is not handled now.)
213 #
214 # Also, some snippets may #undef/#define another define but
215 # they don't "provide" the define as such. For example,
216 # DUK_F_CLANG.h.in undefines DUK_F_GCC defines if clang is
217 # detected: DUK_F_CLANG.h.in is considered to require
218 # DUK_F_GCC but doesn't provide it. Such redefinitions are
219 # marked "/* redefine */" in the snippets. They're best
220 # avoided, of course.
221
222 if autoscan_provides:
223 m = re_line_provides.match(line)
224 if m is not None and '/* redefine */' not in line and \
225 len(m.group(1)) > 0 and m.group(1)[-1] != '_':
226 # Don't allow e.g. DUK_USE_ which results from matching DUK_USE_xxx
227 #print('PROVIDES: %r' % m.group(1))
228 self.provides[m.group(1)] = True
229 if autoscan_requires:
230 matches = re.findall(re_line_requires, line)
231 for m in matches:
232 if len(m) > 0 and m[-1] == '_':
233 # Don't allow e.g. DUK_USE_ which results from matching DUK_USE_xxx
234 pass
235 elif m[:7] == 'DUK_OPT':
236 # DUK_OPT_xxx always come from outside
237 pass
238 elif m[:7] == 'DUK_USE':
239 # DUK_USE_xxx are internal and they should not be 'requirements'
240 pass
241 elif self.provides.has_key(m):
242 # Snippet provides it's own require; omit
243 pass
244 else:
245 #print('REQUIRES: %r' % m)
246 self.requires[m] = True
247
248 def fromFile(cls, filename):
249 lines = []
250 with open(filename, 'rb') as f:
251 for line in f:
252 if line[-1] == '\n':
253 line = line[:-1]
254 lines.append(line)
255 return Snippet(lines, autoscan_requires=True, autoscan_provides=True)
256 fromFile = classmethod(fromFile)
257
258 def merge(cls, snippets):
259 ret = Snippet([], [], [])
260 for s in snippets:
261 ret.lines += s.lines
262 for k in s.provides.keys():
263 ret.provides[k] = True
264 for k in s.requires.keys():
265 ret.requires[k] = True
266 return ret
267 merge = classmethod(merge)
268
269 # Helper for building a text file from individual lines, injected files, etc.
270 # Inserted values are converted to Snippets so that their provides/requires
271 # information can be tracked. When non-C outputs are created, these will be
272 # bogus but ignored.
273 class FileBuilder:
274 vals = None # snippet list
275 base_dir = None
276 use_cpp_warning = False
277
278 def __init__(self, base_dir=None, use_cpp_warning=False):
279 self.vals = []
280 self.base_dir = base_dir
281 self.use_cpp_warning = use_cpp_warning
282
283 def line(self, line):
284 self.vals.append(Snippet([ line ]))
285
286 def lines(self, lines):
287 if len(lines) > 0 and lines[-1] == '\n':
288 lines = lines[:-1] # strip last newline to avoid empty line
289 self.vals.append(Snippet(lines.split('\n')))
290
291 def empty(self):
292 self.vals.append(Snippet([ '' ]))
293
294 def rst_heading(self, title, char, doubled=False):
295 tmp = []
296 if doubled:
297 tmp.append(char * len(title))
298 tmp.append(title)
299 tmp.append(char * len(title))
300 self.vals.append(Snippet(tmp))
301
302 def snippet_relative(self, fn):
303 sn = Snippet.fromFile(os.path.join(self.base_dir, fn))
304 self.vals.append(sn)
305
306 def snippet_absolute(fn):
307 sn = Snippet.fromFile(fn)
308 self.vals.append(sn)
309
310 def cpp_error(self, msg):
311 # XXX: assume no newlines etc
312 self.vals.append(Snippet([ '#error %s' % msg ]))
313
314 def cpp_warning(self, msg):
315 # XXX: assume no newlines etc
316 # XXX: support compiler specific warning mechanisms
317 if self.use_cpp_warning:
318 # C preprocessor '#warning' is often supported
319 self.vals.append(Snippet([ '#warning %s' % msg ]))
320 else:
321 self.vals.append(Snippet([ '/* WARNING: %s */' % msg ]))
322
323 def cpp_warning_or_error(self, msg, is_error=True):
324 if is_error:
325 self.cpp_error(msg)
326 else:
327 self.cpp_warning(msg)
328
329 def chdr_block_heading(self, msg):
330 lines = []
331 lines.append('')
332 lines.append('/*')
333 lines.append(' * ' + msg)
334 lines.append(' */')
335 lines.append('')
336 self.vals.append(Snippet(lines))
337
338 def join(self):
339 tmp = []
340 for line in self.vals:
341 if not isinstance(line, object):
342 raise Exception('self.vals must be all snippets')
343 for x in line.lines: # x is a Snippet
344 tmp.append(x)
345 return '\n'.join(tmp)
346
347 def fill_dependencies_for_snippets(self, idx_deps):
348 fill_dependencies_for_snippets(self.vals, idx_deps)
349
350 # Insert missing define dependencies into index 'idx_deps' repeatedly
351 # until no unsatisfied dependencies exist. This is used to pull in
352 # the required DUK_F_xxx helper defines without pulling them all in.
353 # The resolution mechanism also ensures dependencies are pulled in the
354 # correct order, i.e. DUK_F_xxx helpers may depend on each other (as
355 # long as there are no circular dependencies).
356 #
357 # XXX: this can be simplified a lot
358 def fill_dependencies_for_snippets(snippets, idx_deps):
359 # graph[A] = [ B, ... ] <-> B, ... provide something A requires.
360 graph = {}
361 snlist = []
362 resolved = [] # for printing only
363
364 def add(sn):
365 if sn in snlist:
366 return # already present
367 snlist.append(sn)
368
369 to_add = []
370
371 for k in sn.requires.keys():
372 if assumed_provides.has_key(k):
373 continue
374
375 found = False
376 for sn2 in snlist:
377 if sn2.provides.has_key(k):
378 if not graph.has_key(sn):
379 graph[sn] = []
380 graph[sn].append(sn2)
381 found = True # at least one other node provides 'k'
382
383 if not found:
384 #print 'Resolving %r' % k
385 resolved.append(k)
386
387 # Find a header snippet which provides the missing define.
388 # Some DUK_F_xxx files provide multiple defines, so we don't
389 # necessarily know the snippet filename here.
390
391 sn_req = None
392 for sn2 in helper_snippets:
393 if sn2.provides.has_key(k):
394 sn_req = sn2
395 break
396 if sn_req is None:
397 print(repr(sn.lines))
398 raise Exception('cannot resolve missing require: %r' % k)
399
400 # Snippet may have further unresolved provides; add recursively
401 to_add.append(sn_req)
402
403 if not graph.has_key(sn):
404 graph[sn] = []
405 graph[sn].append(sn_req)
406
407 for sn in to_add:
408 add(sn)
409
410 # Add original snippets. This fills in the required nodes
411 # recursively.
412 for sn in snippets:
413 add(sn)
414
415 # Figure out fill-ins by looking for snippets not in original
416 # list and without any unserialized dependent nodes.
417 handled = {}
418 for sn in snippets:
419 handled[sn] = True
420 keepgoing = True
421 while keepgoing:
422 keepgoing = False
423 for sn in snlist:
424 if handled.has_key(sn):
425 continue
426
427 success = True
428 for dep in graph.get(sn, []):
429 if not handled.has_key(dep):
430 success = False
431 if success:
432 snippets.insert(idx_deps, sn)
433 idx_deps += 1
434 snippets.insert(idx_deps, Snippet([ '' ]))
435 idx_deps += 1
436 handled[sn] = True
437 keepgoing = True
438 break
439
440 # XXX: detect and handle loops cleanly
441 for sn in snlist:
442 if handled.has_key(sn):
443 continue
444 print('UNHANDLED KEY')
445 print('PROVIDES: %r' % sn.provides)
446 print('REQUIRES: %r' % sn.requires)
447 print('\n'.join(sn.lines))
448
449 # print(repr(graph))
450 # print(repr(snlist))
451 print 'Resolved helper defines: %r' % resolved
452
453 def serialize_snippet_list(snippets):
454 ret = []
455
456 emitted_provides = {}
457 for k in assumed_provides.keys():
458 emitted_provides[k] = True
459
460 for sn in snippets:
461 ret += sn.lines
462 for k in sn.provides.keys():
463 emitted_provides[k] = True
464 for k in sn.requires.keys():
465 if not emitted_provides.has_key(k):
466 # XXX: conditional warning, happens in some normal cases
467 #print('WARNING: define %r required, not provided so far' % k)
468 pass
469
470 return '\n'.join(ret)
471
472 def remove_duplicate_newlines(x):
473 ret = []
474 empty = False
475 for line in x.split('\n'):
476 if line == '':
477 if empty:
478 pass
479 else:
480 ret.append(line)
481 empty = True
482 else:
483 empty = False
484 ret.append(line)
485 return '\n'.join(ret)
486
487 def scan_use_defs(dirname):
488 global use_defs, use_defs_list
489 use_defs = {}
490 use_defs_list = []
491
492 for fn in os.listdir(dirname):
493 root, ext = os.path.splitext(fn)
494 if not root.startswith('DUK_USE_') or ext != '.yaml':
495 continue
496 with open(os.path.join(dirname, fn), 'rb') as f:
497 doc = yaml.load(f)
498 if doc.get('example', False):
499 continue
500 if doc.get('unimplemented', False):
501 print('WARNING: unimplemented: %s' % fn)
502 continue
503 dockeys = doc.keys()
504 for k in dockeys:
505 if not k in allowed_use_meta_keys:
506 print('WARNING: unknown key %s in metadata file %s' % (k, fn))
507 for k in required_use_meta_keys:
508 if not k in dockeys:
509 print('WARNING: missing key %s in metadata file %s' % (k, fn))
510
511 use_defs[doc['define']] = doc
512
513 keys = use_defs.keys()
514 keys.sort()
515 for k in keys:
516 use_defs_list.append(use_defs[k])
517
518 def scan_opt_defs(dirname):
519 global opt_defs, opt_defs_list
520 opt_defs = {}
521 opt_defs_list = []
522
523 for fn in os.listdir(dirname):
524 root, ext = os.path.splitext(fn)
525 if not root.startswith('DUK_OPT_') or ext != '.yaml':
526 continue
527 with open(os.path.join(dirname, fn), 'rb') as f:
528 doc = yaml.load(f)
529 if doc.get('example', False):
530 continue
531 if doc.get('unimplemented', False):
532 print('WARNING: unimplemented: %s' % fn)
533 continue
534 dockeys = doc.keys()
535 for k in dockeys:
536 if not k in allowed_opt_meta_keys:
537 print('WARNING: unknown key %s in metadata file %s' % (k, fn))
538 for k in required_opt_meta_keys:
539 if not k in dockeys:
540 print('WARNING: missing key %s in metadata file %s' % (k, fn))
541
542 opt_defs[doc['define']] = doc
543
544 keys = opt_defs.keys()
545 keys.sort()
546 for k in keys:
547 opt_defs_list.append(opt_defs[k])
548
549 def scan_use_tags():
550 global use_tags, use_tags_list
551 use_tags = {}
552
553 for doc in use_defs_list:
554 for tag in doc.get('tags', []):
555 use_tags[tag] = True
556
557 use_tags_list = use_tags.keys()
558 use_tags_list.sort()
559
560 def scan_tags_meta(filename):
561 global tags_meta
562
563 with open(filename, 'rb') as f:
564 tags_meta = yaml.load(f)
565
566 def scan_snippets(dirname):
567 global helper_snippets
568 helper_snippets = []
569
570 for fn in os.listdir(dirname):
571 if (fn[0:6] != 'DUK_F_'):
572 continue
573 #print('Autoscanning snippet: %s' % fn)
574 helper_snippets.append(Snippet.fromFile(os.path.join(dirname, fn)))
575
576 def validate_platform_file(filename):
577 sn = Snippet.fromFile(filename)
578
579 # XXX: move required provides/defines into metadata only
580 for req in platform_required_provides:
581 if req not in sn.provides:
582 raise Exception('Platform %s is missing %s' % (filename, req))
583
584 if not ('DUK_USE_SETJMP' in sn.provides or 'DUK_USE_UNDERSCORE_SETJMP' in sn.provides or
585 'DUK_USE_SIGSETJMP' in sn.provides):
586 raise Exception('Platform %s is missing a setjmp provider' % filename)
587
588 def validate_architecture_file(filename):
589 sn = Snippet.fromFile(filename)
590
591 # XXX: move required provides/defines into metadata only
592 for req in architecture_required_provides:
593 if req not in sn.provides:
594 raise Exception('Architecture %s is missing %s' % (filename, req))
595
596 def validate_compiler_file(filename):
597 sn = Snippet.fromFile(filename)
598
599 # XXX: move required provides/defines into metadata only
600 for req in compiler_required_provides:
601 if req not in sn.provides:
602 raise Exception('Architecture %s is missing %s' % (filename, req))
603
604 def get_tag_title(tag):
605 meta = tags_meta.get(tag, None)
606 if meta is None:
607 return tag
608 else:
609 return meta.get('title', tag)
610
611 def get_tag_description(tag):
612 meta = tags_meta.get(tag, None)
613 if meta is None:
614 return None
615 else:
616 return meta.get('description', None)
617
618 def get_tag_list_with_preferred_order(preferred):
619 tags = []
620
621 # Preferred tags first
622 for tag in preferred:
623 if tag not in tags:
624 tags.append(tag)
625
626 # Remaining tags in alphabetic order
627 for tag in use_tags_list:
628 if tag not in tags:
629 tags.append(tag)
630
631 #print('Effective tag order: %r' % tags)
632 return tags
633
634 def rst_format(text):
635 # XXX: placeholder, need to decide on markup conventions for YAML files
636 ret = []
637 for para in text.split('\n'):
638 if para == '':
639 continue
640 ret.append(para)
641 return '\n\n'.join(ret)
642
643 def cint_encode(x):
644 if not isinstance(x, (int, long)):
645 raise Exception('invalid input: %r' % x)
646
647 # XXX: unsigned constants?
648 if x > 0x7fffffff or x < -0x80000000:
649 return '%dLL' % x
650 elif x > 0x7fff or x < -0x8000:
651 return '%dL' % x
652 else:
653 return '%d' % x
654
655 def cstr_encode(x):
656 if isinstance(x, unicode):
657 x = x.encode('utf-8')
658 if not isinstance(x, str):
659 raise Exception('invalid input: %r' % x)
660
661 res = '"'
662 term = False
663 has_terms = False
664 for c in x:
665 if term:
666 # Avoid ambiguous hex escapes
667 res += '" "'
668 term = False
669 has_terms = True
670 o = ord(c)
671 if o < 0x20 or o > 0x7e or c in '"\\':
672 res += '\\x%02x' % o
673 term = True
674 else:
675 res += c
676 res += '"'
677
678 if has_terms:
679 res = '(' + res + ')'
680
681 return res
682
683 #
684 # Autogeneration of option documentation
685 #
686
687 # Shared helper to generate DUK_OPT_xxx and DUK_USE_xxx documentation.
688 # XXX: unfinished placeholder
689 def generate_option_documentation(opts, opt_list=None, rst_title=None, include_default=False):
690 ret = FileBuilder(use_cpp_warning=opts.use_cpp_warning)
691
692 tags = get_tag_list_with_preferred_order(doc_tag_order)
693
694 title = rst_title
695 ret.rst_heading(title, '=', doubled=True)
696
697 handled = {}
698
699 for tag in tags:
700 first = True
701
702 for doc in opt_list:
703 if tag != doc['tags'][0]: # sort under primary tag
704 continue
705 dname = doc['define']
706 desc = doc.get('description', None)
707
708 if handled.has_key(dname):
709 raise Exception('define handled twice, should not happen: %r' % dname)
710 handled[dname] = True
711
712 if first: # emit tag heading only if there are subsections
713 ret.empty()
714 ret.rst_heading(get_tag_title(tag), '=')
715
716 tag_desc = get_tag_description(tag)
717 if tag_desc is not None:
718 ret.empty()
719 ret.line(rst_format(tag_desc))
720 first = False
721
722 ret.empty()
723 ret.rst_heading(dname, '-')
724
725 if desc is not None:
726 ret.empty()
727 ret.line(rst_format(desc))
728
729 if include_default:
730 ret.empty()
731 ret.line('Default: ``' + str(doc['default']) + '``') # XXX: rst or other format
732
733 for doc in opt_list:
734 dname = doc['define']
735 if not handled.has_key(dname):
736 raise Exception('unhandled define (maybe missing from tags list?): %r' % dname)
737
738 ret.empty()
739 return ret.join()
740
741 def generate_feature_option_documentation(opts):
742 return generate_option_documentation(opts, opt_list=opt_defs_list, rst_title='Duktape feature options', include_default=False)
743
744 def generate_config_option_documentation(opts):
745 return generate_option_documentation(opts, opt_list=use_defs_list, rst_title='Duktape config options', include_default=True)
746
747 #
748 # Helpers for duk_config.h generation
749 #
750
751 def get_forced_options(opts):
752 # Forced options, last occurrence wins (allows a base config file to be
753 # overridden by a more specific one).
754 forced_opts = {}
755 for val in opts.force_options_yaml:
756 doc = yaml.load(StringIO.StringIO(val))
757 for k in doc.keys():
758 if use_defs.has_key(k):
759 pass # key is known
760 else:
761 print 'WARNING: option override key %s not defined in metadata, ignoring' % k
762 forced_opts[k] = doc[k] # shallow copy
763
764 print 'Overrides: %s' % json.dumps(forced_opts)
765
766 return forced_opts
767
768 # Emit a default #define / #undef for an option based on
769 # a config option metadata node (parsed YAML doc).
770 def emit_default_from_config_meta(ret, doc, forced_opts, undef_done):
771 defname = doc['define']
772 defval = forced_opts.get(defname, doc['default'])
773
774 if defval == True:
775 ret.line('#define ' + defname)
776 elif defval == False:
777 if not undef_done:
778 ret.line('#undef ' + defname)
779 else:
780 # Default value is false, and caller has emitted
781 # an unconditional #undef, so don't emit a duplicate
782 pass
783 elif isinstance(defval, (int, long)):
784 # integer value
785 ret.line('#define ' + defname + ' ' + cint_encode(defval))
786 elif isinstance(defval, (str, unicode)):
787 # verbatim value
788 ret.line('#define ' + defname + ' ' + defval)
789 elif isinstance(defval, dict):
790 if defval.has_key('verbatim'):
791 # verbatim text for the entire line
792 ret.line(defval['verbatim'])
793 elif defval.has_key('string'):
794 # C string value
795 ret.line('#define ' + defname + ' ' + cstr_encode(defval['string']))
796 else:
797 raise Exception('unsupported value for option %s: %r' % (defname, defval))
798 else:
799 raise Exception('unsupported value for option %s: %r' % (defname, defval))
800
801 # Add a header snippet for detecting presence of DUK_OPT_xxx feature
802 # options which will be removed in Duktape 2.x.
803 def add_legacy_feature_option_checks(opts, ret):
804 ret.chdr_block_heading('Checks for legacy feature options (DUK_OPT_xxx)')
805
806 defs = []
807 for doc in opt_defs_list:
808 if doc['define'] not in defs:
809 defs.append(doc['define'])
810 for doc in use_defs_list:
811 for dname in doc.get('related_feature_defines', []):
812 if dname not in defs:
813 defs.append(dname)
814 defs.sort()
815
816 for optname in defs:
817 suggested = []
818 for doc in use_defs_list:
819 if optname in doc.get('related_feature_defines', []):
820 suggested.append(doc['define'])
821 ret.empty()
822 ret.line('#if defined(%s)' % optname)
823 if len(suggested) > 0:
824 ret.cpp_warning_or_error('unsupported legacy feature option %s used, consider options: %s' % (optname, ', '.join(suggested)), opts.sanity_strict)
825 else:
826 ret.cpp_warning_or_error('unsupported legacy feature option %s used' % optname, opts.sanity_strict)
827 ret.line('#endif')
828
829 ret.empty()
830
831 # Add a header snippet for checking consistency of DUK_USE_xxx config
832 # options, e.g. inconsistent options, invalid option values.
833 def add_config_option_checks(opts, ret):
834 ret.chdr_block_heading('Checks for config option consistency (DUK_USE_xxx)')
835
836 defs = []
837 for doc in use_defs_list:
838 if doc['define'] not in defs:
839 defs.append(doc['define'])
840 defs.sort()
841
842 for optname in defs:
843 doc = use_defs[optname]
844 dname = doc['define']
845
846 # XXX: more checks
847
848 if doc.get('removed', None) is not None:
849 ret.empty()
850 ret.line('#if defined(%s)' % dname)
851 ret.cpp_warning_or_error('unsupported config option used (option has been removed): %s' % dname, opts.sanity_strict)
852 ret.line('#endif')
853 elif doc.get('deprecated', None) is not None:
854 ret.empty()
855 ret.line('#if defined(%s)' % dname)
856 ret.cpp_warning_or_error('unsupported config option used (option has been deprecated): %s' % dname, opts.sanity_strict)
857 ret.line('#endif')
858
859 for req in doc.get('requires', []):
860 ret.empty()
861 ret.line('#if defined(%s) && !defined(%s)' % (dname, req))
862 ret.cpp_warning_or_error('config option %s requires option %s (which is missing)' % (dname, req), opts.sanity_strict)
863 ret.line('#endif')
864
865 for req in doc.get('conflicts', []):
866 ret.empty()
867 ret.line('#if defined(%s) && defined(%s)' % (dname, req))
868 ret.cpp_warning_or_error('config option %s conflicts with option %s (which is also defined)' % (dname, req), opts.sanity_strict)
869 ret.line('#endif')
870
871 ret.empty()
872
873 # Add a header snippet for providing a __OVERRIDE_DEFINES__ section.
874 def add_override_defines_section(opts, ret):
875 ret.empty()
876 ret.line('/*')
877 ret.line(' * You may add overriding #define/#undef directives below for')
878 ret.line(' * customization. You of course cannot un-#include or un-typedef')
879 ret.line(' * anything; these require direct changes above.')
880 ret.line(' */')
881 ret.empty()
882 ret.line('/* __OVERRIDE_DEFINES__ */')
883 ret.empty()
884
885 # Add automatic DUK_OPT_XXX and DUK_OPT_NO_XXX handling for backwards
886 # compatibility with Duktape 1.2 and before.
887 def add_feature_option_handling(opts, ret, forced_opts):
888 ret.chdr_block_heading('Feature option handling')
889
890 for doc in use_defs_list:
891 # If a related feature option exists, it can be used to force
892 # enable/disable the target feature. If neither feature option
893 # (DUK_OPT_xxx or DUK_OPT_NO_xxx) is given, revert to default.
894
895 config_define = doc['define']
896
897 feature_define = None
898 feature_no_define = None
899 inverted = False
900 if doc.has_key('feature_enables'):
901 feature_define = doc['feature_enables']
902 elif doc.has_key('feature_disables'):
903 feature_define = doc['feature_disables']
904 inverted = True
905 else:
906 pass
907
908 if feature_define is not None:
909 feature_no_define = 'DUK_OPT_NO_' + feature_define[8:]
910 ret.line('#if defined(%s)' % feature_define)
911 if inverted:
912 ret.line('#undef %s' % config_define)
913 else:
914 ret.line('#define %s' % config_define)
915 ret.line('#elif defined(%s)' % feature_no_define)
916 if inverted:
917 ret.line('#define %s' % config_define)
918 else:
919 ret.line('#undef %s' % config_define)
920 ret.line('#else')
921 undef_done = False
922 emit_default_from_config_meta(ret, doc, forced_opts, undef_done)
923 ret.line('#endif')
924 elif doc.has_key('feature_snippet'):
925 ret.lines(doc['feature_snippet'])
926 else:
927 pass
928
929 ret.empty()
930
931 ret.empty()
932
933 # Development time helper: add DUK_ACTIVE which provides a runtime C string
934 # indicating what DUK_USE_xxx config options are active at run time. This
935 # is useful in genconfig development so that one can e.g. diff the active
936 # run time options of two headers. This is intended just for genconfig
937 # development and is not available in normal headers.
938 def add_duk_active_defines_macro(ret):
939 ret.chdr_block_heading('DUK_ACTIVE_DEFINES macro (development only)')
940
941 idx = 0
942 for doc in use_defs_list:
943 defname = doc['define']
944
945 ret.line('#if defined(%s)' % defname)
946 ret.line('#define DUK_ACTIVE_DEF%d " %s"' % (idx, defname))
947 ret.line('#else')
948 ret.line('#define DUK_ACTIVE_DEF%d ""' % idx)
949 ret.line('#endif')
950
951 idx += 1
952
953 tmp = []
954 for i in xrange(idx):
955 tmp.append('DUK_ACTIVE_DEF%d' % i)
956
957 ret.line('#define DUK_ACTIVE_DEFINES ("Active: ["' + ' '.join(tmp) + ' " ]")')
958
959 #
960 # duk_config.h generation
961 #
962
963 # Generate the default duk_config.h which provides automatic detection of
964 # platform, compiler, architecture, and features for major platforms.
965 # Use manually written monolithic header snippets from Duktape 1.2 for
966 # generating the header. This header is Duktape 1.2 compatible and supports
967 # DUK_OPT_xxx feature options. Later on the DUK_OPT_xxx options will be
968 # removed and users can override DUK_USE_xxx flags directly by modifying
969 # duk_config.h or by generating a new header using genconfig.
970 def generate_autodetect_duk_config_header(opts, meta_dir):
971 ret = FileBuilder(base_dir=os.path.join(meta_dir, 'header-snippets'), \
972 use_cpp_warning=opts.use_cpp_warning)
973
974 forced_opts = get_forced_options(opts)
975
976 ret.snippet_relative('comment_prologue.h.in')
977 ret.empty()
978
979 ret.line('#ifndef DUK_CONFIG_H_INCLUDED')
980 ret.line('#define DUK_CONFIG_H_INCLUDED')
981 ret.empty()
982
983 # Compiler features, processor/architecture, OS, compiler
984 ret.snippet_relative('compiler_features.h.in')
985 ret.empty()
986 ret.snippet_relative('rdtsc.h.in') # XXX: move downwards
987 ret.empty()
988 ret.snippet_relative('platform1.h.in')
989 ret.empty()
990
991 # Feature selection, system include, Date provider
992 # Most #include statements are here
993 ret.snippet_relative('platform2.h.in')
994 ret.empty()
995 ret.snippet_relative('ullconsts.h.in')
996 ret.empty()
997 ret.snippet_relative('libc.h.in')
998 ret.empty()
999
1000 # Number types
1001 ret.snippet_relative('types1.h.in')
1002 ret.line('#if defined(DUK_F_HAVE_INTTYPES)')
1003 ret.line('/* C99 or compatible */')
1004 ret.empty()
1005 ret.snippet_relative('types_c99.h.in')
1006 ret.empty()
1007 ret.line('#else /* C99 types */')
1008 ret.empty()
1009 ret.snippet_relative('types_legacy.h.in')
1010 ret.empty()
1011 ret.line('#endif /* C99 types */')
1012 ret.empty()
1013 ret.snippet_relative('types2.h.in')
1014 ret.empty()
1015 ret.snippet_relative('64bitops.h.in')
1016 ret.empty()
1017
1018 # Alignment
1019 ret.snippet_relative('alignment.h.in')
1020 ret.empty()
1021
1022 # Object layout
1023 ret.snippet_relative('object_layout.h.in')
1024 ret.empty()
1025
1026 # Byte order
1027 ret.snippet_relative('byteorder.h.in')
1028 ret.empty()
1029
1030 # Packed duk_tval
1031 ret.snippet_relative('packed_tval.h.in')
1032 ret.empty()
1033
1034 # Detect 'fast math'
1035 ret.snippet_relative('reject_fast_math.h.in')
1036 ret.empty()
1037
1038 # IEEE double constants
1039 ret.snippet_relative('double_const.h.in')
1040 ret.empty()
1041
1042 # Math and other ANSI replacements, NetBSD workaround, paranoid math, paranoid Date
1043 ret.snippet_relative('repl_math.h.in')
1044 ret.empty()
1045 ret.snippet_relative('paranoid_date.h.in')
1046 ret.empty()
1047 ret.snippet_relative('repl_ansi.h.in')
1048 ret.empty()
1049
1050 # Platform function pointers
1051 ret.snippet_relative('platform_funcptr.h.in')
1052 ret.empty()
1053
1054 # General compiler stuff
1055 ret.snippet_relative('stringify.h.in')
1056 ret.empty()
1057 ret.snippet_relative('segfault.h.in')
1058 ret.empty()
1059 ret.snippet_relative('unreferenced.h.in')
1060 ret.empty()
1061 ret.snippet_relative('noreturn.h.in')
1062 ret.empty()
1063 ret.snippet_relative('unreachable.h.in')
1064 ret.empty()
1065 ret.snippet_relative('likely.h.in')
1066 ret.empty()
1067 ret.snippet_relative('inline.h.in')
1068 ret.empty()
1069 ret.snippet_relative('visibility.h.in')
1070 ret.empty()
1071 ret.snippet_relative('file_line_func.h.in')
1072 ret.empty()
1073 ret.snippet_relative('byteswap.h.in')
1074 ret.empty()
1075
1076 # Arhitecture, OS, and compiler strings
1077 ret.snippet_relative('arch_string.h.in')
1078 ret.empty()
1079 ret.snippet_relative('os_string.h.in')
1080 ret.empty()
1081 ret.snippet_relative('compiler_string.h.in')
1082 ret.empty()
1083
1084 # Target info
1085 ret.snippet_relative('target_info.h.in')
1086 ret.empty()
1087
1088 # Longjmp handling
1089 ret.snippet_relative('longjmp.h.in')
1090 ret.empty()
1091
1092 # Unsorted flags, contains almost all actual Duktape-specific
1093 # but platform independent features
1094 ret.snippet_relative('unsorted_flags.h.in')
1095 ret.empty()
1096
1097 # User declarations
1098 ret.snippet_relative('user_declare.h.in')
1099 ret.empty()
1100
1101 # Emit forced options. If a corresponding option is already defined
1102 # by a snippet above, #undef it first.
1103
1104 tmp = Snippet(ret.join().split('\n'))
1105 first_forced = True
1106 for doc in use_defs_list:
1107 defname = doc['define']
1108
1109 if doc.get('removed', None) is not None and opts.omit_removed_config_options:
1110 continue
1111 if doc.get('deprecated', None) is not None and opts.omit_deprecated_config_options:
1112 continue
1113 if doc.get('unused', False) == True and opts.omit_unused_config_options:
1114 continue
1115 if not forced_opts.has_key(defname):
1116 continue
1117
1118 if not doc.has_key('default'):
1119 raise Exception('config option %s is missing default value' % defname)
1120
1121 if first_forced:
1122 ret.chdr_block_heading('Forced options')
1123 first_forced = False
1124
1125 undef_done = False
1126 if tmp.provides.has_key(defname):
1127 ret.line('#undef ' + defname)
1128 undef_done = True
1129
1130 emit_default_from_config_meta(ret, doc, forced_opts, undef_done)
1131
1132 ret.empty()
1133
1134 # If manually-edited snippets don't #define or #undef a certain
1135 # config option, emit a default value here. This is useful to
1136 # fill-in for new config options not covered by manual snippets
1137 # (which is intentional).
1138
1139 tmp = Snippet(ret.join().split('\n'))
1140 need = {}
1141 for doc in use_defs_list:
1142 if doc.get('removed', None) is not None: # XXX: check version
1143 continue
1144 need[doc['define']] = True
1145 for k in tmp.provides.keys():
1146 if need.has_key(k):
1147 del need[k]
1148 need_keys = sorted(need.keys())
1149
1150 if len(need_keys) > 0:
1151 ret.chdr_block_heading('Autogenerated defaults')
1152
1153 for k in need_keys:
1154 #print('config option %s not covered by manual snippets, emitting default automatically' % k)
1155 emit_default_from_config_meta(ret, use_defs[k], {}, False)
1156
1157 ret.empty()
1158
1159 ret.snippet_relative('custom_header.h.in')
1160 ret.empty()
1161
1162 if len(opts.fixup_header_lines) > 0:
1163 ret.chdr_block_heading('Fixups')
1164 for line in opts.fixup_header_lines:
1165 ret.line(line)
1166 ret.empty()
1167
1168 add_override_defines_section(opts, ret)
1169
1170 # Date provider snippet is after custom header and overrides, so that
1171 # the user may define e.g. DUK_USE_DATE_NOW_GETTIMEOFDAY in their
1172 # custom header.
1173 ret.snippet_relative('date_provider.h.in')
1174 ret.empty()
1175
1176 # Sanity checks
1177 # XXX: use autogenerated sanity checks instead
1178 ret.snippet_relative('sanity.h.in')
1179 ret.empty()
1180
1181 if opts.emit_legacy_feature_check:
1182 # XXX: this doesn't really make sense for the autodetect
1183 # header yet, because sanity.h.in already covers these.
1184 add_legacy_feature_option_checks(opts, ret)
1185 if opts.emit_config_sanity_check:
1186 add_config_option_checks(opts, ret)
1187 if opts.add_active_defines_macro:
1188 add_duk_active_defines_macro(ret)
1189
1190 ret.line('#endif /* DUK_CONFIG_H_INCLUDED */')
1191 ret.empty() # for trailing newline
1192 return remove_duplicate_newlines(ret.join())
1193
1194 # Generate a duk_config.h where platform, architecture, and compiler are
1195 # all either autodetected or specified by user. When autodetection is
1196 # used, the generated header is based on modular snippets and metadata to
1197 # be more easily maintainable than manually edited monolithic snippets.
1198 #
1199 # This approach will replace the legacy autodetect header in Duktape 1.4,
1200 # and most likely the separate barebones header also.
1201 #
1202 # The generated header is Duktape 1.2 compatible for now, and supports
1203 # DUK_OPT_xxx feature options. Later on the DUK_OPT_xxx options will be
1204 # removed and user code overrides DUK_USE_xxx flags directly by modifying
1205 # duk_config.h manually or by generating a new header using genconfig.
1206 def generate_autodetect_duk_config_header_modular(opts, meta_dir):
1207 ret = FileBuilder(base_dir=os.path.join(meta_dir, 'header-snippets'), \
1208 use_cpp_warning=opts.use_cpp_warning)
1209
1210 forced_opts = get_forced_options(opts)
1211
1212 platforms = None
1213 with open(os.path.join(meta_dir, 'platforms.yaml'), 'rb') as f:
1214 platforms = yaml.load(f)
1215 architectures = None
1216 with open(os.path.join(meta_dir, 'architectures.yaml'), 'rb') as f:
1217 architectures = yaml.load(f)
1218 compilers = None
1219 with open(os.path.join(meta_dir, 'compilers.yaml'), 'rb') as f:
1220 compilers = yaml.load(f)
1221
1222 ret.line('/*')
1223 ret.line(' * duk_config.h autodetect header generated by genconfig.py.')
1224 ret.line(' *')
1225 ret.line(' * Git commit: %s' % opts.git_commit or 'n/a')
1226 ret.line(' * Git describe: %s' % opts.git_describe or 'n/a')
1227 ret.line(' *')
1228 if opts.platform is not None:
1229 ret.line(' * Platform: ' + opts.platform)
1230 else:
1231 ret.line(' * Supported platforms:')
1232 for platf in platforms['autodetect']:
1233 ret.line(' * - %s' % platf.get('name', platf.get('check')))
1234 ret.line(' *')
1235 if opts.architecture is not None:
1236 ret.line(' * Architecture: ' + opts.architecture)
1237 else:
1238 ret.line(' * Supported architectures:')
1239 for arch in architectures['autodetect']:
1240 ret.line(' * - %s' % arch.get('name', arch.get('check')))
1241 ret.line(' *')
1242 if opts.compiler is not None:
1243 ret.line(' * Compiler: ' + opts.compiler)
1244 else:
1245 ret.line(' * Supported compilers:')
1246 for comp in compilers['autodetect']:
1247 ret.line(' * - %s' % comp.get('name', comp.get('check')))
1248 ret.line(' *')
1249 ret.line(' */')
1250 ret.empty()
1251 ret.line('#ifndef DUK_CONFIG_H_INCLUDED')
1252 ret.line('#define DUK_CONFIG_H_INCLUDED')
1253 ret.empty()
1254
1255 ret.chdr_block_heading('Intermediate helper defines')
1256
1257 idx_deps = len(ret.vals) # position where to emit dependencies
1258
1259 # Feature selection, system include, Date provider
1260 # Most #include statements are here
1261
1262 if opts.platform is not None:
1263 ret.chdr_block_heading('Platform: ' + opts.platform)
1264
1265 ret.snippet_relative('platform_cppextras.h.in')
1266 ret.empty()
1267
1268 # XXX: better to lookup platforms metadata
1269 include = 'platform_%s.h.in' % opts.platform
1270 validate_platform_file(os.path.join(meta_dir, 'header-snippets', include))
1271 ret.snippet_relative(include)
1272 else:
1273 ret.chdr_block_heading('Platform autodetection')
1274
1275 ret.snippet_relative('platform_cppextras.h.in')
1276 ret.empty()
1277
1278 for idx, platf in enumerate(platforms['autodetect']):
1279 check = platf.get('check', None)
1280 include = platf['include']
1281 validate_platform_file(os.path.join(meta_dir, 'header-snippets', include))
1282
1283 if idx == 0:
1284 ret.line('#if defined(%s)' % check)
1285 else:
1286 if check is None:
1287 ret.line('#else')
1288 else:
1289 ret.line('#elif defined(%s)' % check)
1290 ret.snippet_relative(include)
1291 ret.line('#endif /* autodetect platform */')
1292
1293 ret.snippet_relative('platform_sharedincludes.h.in')
1294 ret.empty()
1295
1296 if opts.architecture is not None:
1297 ret.chdr_block_heading('Architecture: ' + opts.architecture)
1298
1299 # XXX: better to lookup architectures metadata
1300 include = 'architecture_%s.h.in' % opts.architecture
1301 validate_architecture_file(os.path.join(meta_dir, 'header-snippets', include))
1302 ret.snippet_relative(include)
1303 else:
1304 ret.chdr_block_heading('Architecture autodetection')
1305
1306 for idx, arch in enumerate(architectures['autodetect']):
1307 check = arch.get('check', None)
1308 include = arch['include']
1309 validate_architecture_file(os.path.join(meta_dir, 'header-snippets', include))
1310
1311 if idx == 0:
1312 ret.line('#if defined(%s)' % check)
1313 else:
1314 if check is None:
1315 ret.line('#else')
1316 else:
1317 ret.line('#elif defined(%s)' % check)
1318 ret.snippet_relative(include)
1319 ret.line('#endif /* autodetect architecture */')
1320
1321 if opts.compiler is not None:
1322 ret.chdr_block_heading('Compiler: ' + opts.compiler)
1323
1324 # XXX: better to lookup compilers metadata
1325 include = 'compiler_%s.h.in' % opts.compiler
1326 validate_compiler_file(os.path.join(meta_dir, 'header-snippets', include))
1327 ret.snippet_relative(include)
1328 else:
1329 ret.chdr_block_heading('Compiler autodetection')
1330
1331 for idx, comp in enumerate(compilers['autodetect']):
1332 check = comp.get('check', None)
1333 include = comp['include']
1334 validate_compiler_file(os.path.join(meta_dir, 'header-snippets', include))
1335
1336 if idx == 0:
1337 ret.line('#if defined(%s)' % check)
1338 else:
1339 if check is None:
1340 ret.line('#else')
1341 else:
1342 ret.line('#elif defined(%s)' % check)
1343 ret.snippet_relative(include)
1344 ret.line('#endif /* autodetect compiler */')
1345
1346 # FIXME: The snippets below have some conflicts with the platform,
1347 # architecture, and compiler snippets files included above. These
1348 # need to be resolved so that (a) each define is only provided from
1349 # one place or (b) the latter definition is a "fill-in" which is
1350 # only used when a certain define is missing from e.g. a compiler
1351 # snippet (useful for e.g. compiler defines which have sane, standard
1352 # defaults).
1353
1354 # FIXME: __uclibc__ needs stdlib.h, but it really is the only exception
1355 ret.snippet_relative('libc.h.in')
1356 ret.empty()
1357
1358 # Number types
1359 ret.snippet_relative('types1.h.in')
1360 ret.line('#if defined(DUK_F_HAVE_INTTYPES)')
1361 ret.line('/* C99 or compatible */')
1362 ret.empty()
1363 ret.snippet_relative('types_c99.h.in')
1364 ret.empty()
1365 ret.line('#else /* C99 types */')
1366 ret.empty()
1367 ret.snippet_relative('types_legacy.h.in')
1368 ret.empty()
1369 ret.line('#endif /* C99 types */')
1370 ret.empty()
1371 ret.snippet_relative('types2.h.in')
1372 ret.empty()
1373 ret.snippet_relative('64bitops.h.in')
1374 ret.empty()
1375
1376 # Alignment
1377 ret.snippet_relative('alignment.h.in')
1378 ret.empty()
1379
1380 # Object layout
1381 ret.snippet_relative('object_layout.h.in')
1382 ret.empty()
1383
1384 # Byte order
1385 # FIXME: from the architecture snippet
1386 ret.snippet_relative('byteorder.h.in')
1387 ret.empty()
1388
1389 # Packed duk_tval
1390 # FIXME: from the architecture snippet
1391 ret.snippet_relative('packed_tval.h.in')
1392 ret.empty()
1393
1394 # Detect 'fast math'
1395 ret.snippet_relative('reject_fast_math.h.in')
1396 ret.empty()
1397
1398 # IEEE double constants
1399 # FIXME: these should maybe be 'fill-ins' if previous headers
1400 # didn't provide something
1401 ret.snippet_relative('double_const.h.in')
1402 ret.empty()
1403
1404 # Math and other ANSI replacements, NetBSD workaround, paranoid math, paranoid Date
1405 ret.snippet_relative('repl_math.h.in')
1406 ret.empty()
1407 ret.snippet_relative('paranoid_date.h.in')
1408 ret.empty()
1409 ret.snippet_relative('repl_ansi.h.in')
1410 ret.empty()
1411
1412 # Platform function pointers
1413 ret.snippet_relative('platform_funcptr.h.in')
1414 ret.empty()
1415
1416 # General compiler stuff
1417 ret.snippet_relative('stringify.h.in')
1418 ret.empty()
1419 ret.snippet_relative('segfault.h.in')
1420 ret.empty()
1421 ret.snippet_relative('unreferenced.h.in')
1422 ret.empty()
1423 ret.snippet_relative('noreturn.h.in')
1424 ret.empty()
1425 ret.snippet_relative('unreachable.h.in')
1426 ret.empty()
1427 ret.snippet_relative('likely.h.in')
1428 ret.empty()
1429 ret.snippet_relative('inline.h.in')
1430 ret.empty()
1431 ret.snippet_relative('visibility.h.in')
1432 ret.empty()
1433 ret.snippet_relative('file_line_func.h.in')
1434 ret.empty()
1435 ret.snippet_relative('byteswap.h.in')
1436 ret.empty()
1437
1438 # These come directly from platform, architecture, and compiler
1439 # snippets.
1440 #ret.snippet_relative('arch_string.h.in')
1441 #ret.empty()
1442 #ret.snippet_relative('os_string.h.in')
1443 #ret.empty()
1444 #ret.snippet_relative('compiler_string.h.in')
1445 #ret.empty()
1446 #ret.snippet_relative('longjmp.h.in')
1447 #ret.empty()
1448
1449 # Target info
1450 ret.snippet_relative('target_info.h.in')
1451 ret.empty()
1452
1453 # Automatic DUK_OPT_xxx feature option handling
1454 # FIXME: platform setjmp/longjmp defines now conflict with this
1455 if True:
1456 # Unsorted flags, contains almost all actual Duktape-specific
1457 # but platform independent features
1458 #ret.snippet_relative('unsorted_flags.h.in')
1459 #ret.empty()
1460
1461 add_feature_option_handling(opts, ret, forced_opts)
1462
1463 ret.snippet_relative('user_declare.h.in')
1464 ret.empty()
1465
1466 # Emit forced options. If a corresponding option is already defined
1467 # by a snippet above, #undef it first.
1468
1469 tmp = Snippet(ret.join().split('\n'))
1470 first_forced = True
1471 for doc in use_defs_list:
1472 defname = doc['define']
1473
1474 if doc.get('removed', None) is not None and opts.omit_removed_config_options:
1475 continue
1476 if doc.get('deprecated', None) is not None and opts.omit_deprecated_config_options:
1477 continue
1478 if doc.get('unused', False) == True and opts.omit_unused_config_options:
1479 continue
1480 if not forced_opts.has_key(defname):
1481 continue
1482
1483 if not doc.has_key('default'):
1484 raise Exception('config option %s is missing default value' % defname)
1485
1486 if first_forced:
1487 ret.chdr_block_heading('Forced options')
1488 first_forced = False
1489
1490 undef_done = False
1491 if tmp.provides.has_key(defname):
1492 ret.line('#undef ' + defname)
1493 undef_done = True
1494
1495 emit_default_from_config_meta(ret, doc, forced_opts, undef_done)
1496
1497 ret.empty()
1498
1499 # If manually-edited snippets don't #define or #undef a certain
1500 # config option, emit a default value here. This is useful to
1501 # fill-in for new config options not covered by manual snippets
1502 # (which is intentional).
1503
1504 tmp = Snippet(ret.join().split('\n'))
1505 need = {}
1506 for doc in use_defs_list:
1507 if doc.get('removed', None) is not None: # XXX: check version
1508 continue
1509 need[doc['define']] = True
1510 for k in tmp.provides.keys():
1511 if need.has_key(k):
1512 del need[k]
1513 need_keys = sorted(need.keys())
1514
1515 if len(need_keys) > 0:
1516 ret.chdr_block_heading('Autogenerated defaults')
1517
1518 for k in need_keys:
1519 #print('config option %s not covered by manual snippets, emitting default automatically' % k)
1520 emit_default_from_config_meta(ret, use_defs[k], {}, False)
1521
1522 ret.empty()
1523
1524 ret.snippet_relative('custom_header.h.in')
1525 ret.empty()
1526
1527 if len(opts.fixup_header_lines) > 0:
1528 ret.chdr_block_heading('Fixups')
1529 for line in opts.fixup_header_lines:
1530 ret.line(line)
1531 ret.empty()
1532
1533 add_override_defines_section(opts, ret)
1534
1535 # Date provider snippet is after custom header and overrides, so that
1536 # the user may define e.g. DUK_USE_DATE_NOW_GETTIMEOFDAY in their
1537 # custom header.
1538 ret.snippet_relative('date_provider.h.in')
1539 ret.empty()
1540
1541 ret.fill_dependencies_for_snippets(idx_deps)
1542
1543 # FIXME: use autogenerated sanity instead of sanity.h.in
1544
1545 ret.snippet_relative('sanity.h.in')
1546 ret.empty()
1547
1548 if opts.emit_legacy_feature_check:
1549 # FIXME: this doesn't really make sense for the autodetect header yet
1550 add_legacy_feature_option_checks(opts, ret)
1551 if opts.emit_config_sanity_check:
1552 add_config_option_checks(opts, ret)
1553 if opts.add_active_defines_macro:
1554 add_duk_active_defines_macro(ret)
1555
1556 ret.line('#endif /* DUK_CONFIG_H_INCLUDED */')
1557 ret.empty() # for trailing newline
1558 return remove_duplicate_newlines(ret.join())
1559
1560 # Generate a barebones duk_config.h header for a specific platform, architecture,
1561 # and compiler. The header won't do automatic feature detection and does not
1562 # support DUK_OPT_xxx feature options (which will be removed in Duktape 2.x).
1563 # Users can then modify this barebones header for very exotic platforms and manage
1564 # the needed changes either as a YAML file or by appending a fixup header snippet.
1565 #
1566 # XXX: to be replaced by generate_modular_duk_config_header().
1567 def generate_barebones_duk_config_header(opts, meta_dir):
1568 ret = FileBuilder(base_dir=os.path.join(meta_dir, 'header-snippets'), \
1569 use_cpp_warning=opts.use_cpp_warning)
1570
1571 # XXX: Provide more defines from YAML config files so that such
1572 # defines can be overridden more conveniently (e.g. DUK_COS).
1573
1574 forced_opts = get_forced_options(opts)
1575
1576 ret.line('/*')
1577 ret.line(' * duk_config.h generated by genconfig.py for:')
1578 ret.line(' * platform: %s' % opts.platform)
1579 ret.line(' * compiler: %s' % opts.compiler)
1580 ret.line(' * architecture: %s' % opts.architecture)
1581 ret.line(' *')
1582 ret.line(' * Git commit: %s' % opts.git_commit or 'n/a')
1583 ret.line(' * Git describe: %s' % opts.git_describe or 'n/a')
1584 ret.line(' */')
1585 ret.empty()
1586 ret.line('#ifndef DUK_CONFIG_H_INCLUDED')
1587 ret.line('#define DUK_CONFIG_H_INCLUDED')
1588
1589 ret.chdr_block_heading('Intermediate helper defines')
1590
1591 idx_deps = len(ret.vals) # position where to emit dependencies
1592
1593 ret.chdr_block_heading('Platform headers and typedefs')
1594
1595 if opts.platform is None:
1596 raise Exception('no platform specified')
1597
1598 fn = 'platform_%s.h.in' % opts.platform
1599 ret.snippet_relative(fn)
1600 ret.empty()
1601 ret.snippet_relative('types_c99.h.in') # XXX: C99 typedefs forced for now
1602 ret.snippet_relative('types2.h.in') # XXX: boilerplate type stuff
1603
1604 ret.chdr_block_heading('Platform features')
1605
1606 # XXX: double constants
1607 # XXX: replacement functions
1608 # XXX: inherit definitions (like '#define DUK_FFLUSH fflush') from a
1609 # generic set of defaults, allow platform configs to override
1610
1611 ret.snippet_relative('platform_generic.h.in')
1612
1613 ret.chdr_block_heading('Compiler features')
1614
1615 if opts.compiler is None:
1616 raise Exception('no compiler specified')
1617
1618 fn = 'compiler_%s.h.in' % opts.compiler
1619 ret.snippet_relative(fn)
1620
1621 # noreturn, vacopy, etc
1622 # visibility attributes
1623
1624 ret.chdr_block_heading('Architecture features')
1625
1626 if opts.architecture is None:
1627 raise Exception('no architecture specified')
1628
1629 fn = 'architecture_%s.h.in' % opts.architecture
1630 ret.snippet_relative(fn)
1631
1632 ret.chdr_block_heading('Config options')
1633
1634 tags = get_tag_list_with_preferred_order(header_tag_order)
1635
1636 handled = {}
1637
1638 # Mark all defines 'provided' by the snippets so far as handled.
1639 # For example, if the system header provides a DUK_USE_OS_STRING,
1640 # we won't emit it again below with its default value (but will
1641 # emit an override value if specified).
1642
1643 for sn in ret.vals:
1644 for k in sn.provides.keys():
1645 handled[k] = True
1646
1647 for tag in tags:
1648 ret.line('/* ' + get_tag_title(tag) + ' */')
1649
1650 for doc in use_defs_list:
1651 defname = doc['define']
1652
1653 if doc.get('removed', None) is not None and opts.omit_removed_config_options:
1654 continue
1655 if doc.get('deprecated', None) is not None and opts.omit_deprecated_config_options:
1656 continue
1657 if doc.get('unused', False) == True and opts.omit_unused_config_options:
1658 continue
1659
1660 if tag != doc['tags'][0]: # sort under primary tag
1661 continue
1662
1663 if not doc.has_key('default'):
1664 raise Exception('config option %s is missing default value' % defname)
1665
1666 undef_done = False
1667
1668 if handled.has_key(defname):
1669 defval = forced_opts.get(defname, None)
1670 if defval is None:
1671 ret.line('/* %s already emitted above */' % defname)
1672 continue
1673
1674 # Define already emitted by snippets above but
1675 # an explicit override wants to redefine it.
1676 # Undef first and then use shared handler to
1677 # setup the forced value.
1678 ret.line('#undef ' + defname)
1679 undef_done = True
1680
1681 # FIXME: macro args; DUK_USE_USER_DECLARE vs. DUK_USE_USER_DECLARE()
1682 # vs. DUK_USE_USER_DECLARE(x,y)
1683
1684 handled[defname] = True
1685 emit_default_from_config_meta(ret, doc, forced_opts, undef_done)
1686
1687 ret.empty()
1688
1689 if len(opts.fixup_header_lines) > 0:
1690 ret.chdr_block_heading('Fixups')
1691 for line in opts.fixup_header_lines:
1692 ret.line(line)
1693
1694 add_override_defines_section(opts, ret)
1695
1696 # Date provider snippet is after custom header and overrides, so that
1697 # the user may define e.g. DUK_USE_DATE_NOW_GETTIMEOFDAY in their
1698 # custom header.
1699 ret.empty()
1700 ret.snippet_relative('date_provider.h.in')
1701 ret.empty()
1702
1703 ret.fill_dependencies_for_snippets(idx_deps)
1704
1705 # XXX: ensure no define is unhandled at the end
1706
1707 # Check for presence of legacy feature options (DUK_OPT_xxx),
1708 # and consistency of final DUK_USE_xxx options.
1709 #
1710 # These could also be emitted into Duktape source code, but it's
1711 # probably better that the checks can be easily disabled from
1712 # duk_config.h.
1713
1714 if opts.emit_legacy_feature_check:
1715 add_legacy_feature_option_checks(opts, ret)
1716 if opts.emit_config_sanity_check:
1717 add_config_option_checks(opts, ret)
1718 if opts.add_active_defines_macro:
1719 add_duk_active_defines_macro(ret)
1720
1721 ret.line('#endif /* DUK_CONFIG_H_INCLUDED */')
1722 ret.empty() # for trailing newline
1723
1724 return remove_duplicate_newlines(serialize_snippet_list(ret.vals)) # XXX: refactor into FileBuilder
1725
1726 #
1727 # Main
1728 #
1729
1730 def main():
1731 # Forced options from multiple sources are gathered into a shared list
1732 # so that the override order remains the same as on the command line.
1733 force_options_yaml = []
1734 def add_force_option_yaml(option, opt, value, parser):
1735 # XXX: check that YAML parses
1736 force_options_yaml.append(value)
1737 def add_force_option_file(option, opt, value, parser):
1738 # XXX: check that YAML parses
1739 with open(value, 'rb') as f:
1740 force_options_yaml.append(f.read())
1741 def add_force_option_define(option, opt, value, parser):
1742 tmp = value.split('=')
1743 if len(tmp) == 1:
1744 doc = { tmp[0]: True }
1745 elif len(tmp) == 2:
1746 doc = { tmp[0]: tmp[1] }
1747 else:
1748 raise Exception('invalid option value: %r' % value)
1749 force_options_yaml.append(yaml.safe_dump(doc))
1750 def add_force_option_undefine(option, opt, value, parser):
1751 tmp = value.split('=')
1752 if len(tmp) == 1:
1753 doc = { tmp[0]: False }
1754 else:
1755 raise Exception('invalid option value: %r' % value)
1756 force_options_yaml.append(yaml.safe_dump(doc))
1757
1758 fixup_header_lines = []
1759 def add_fixup_header_line(option, opt, value, parser):
1760 fixup_header_lines.append(value)
1761 def add_fixup_header_file(option, opt, value, parser):
1762 with open(value, 'rb') as f:
1763 for line in f:
1764 if line[-1] == '\n':
1765 line = line[:-1]
1766 fixup_header_lines.append(line)
1767
1768 commands = [
1769 'autodetect-header',
1770 'barebones-header',
1771 'feature-documentation',
1772 'config-documentation'
1773 ]
1774 parser = optparse.OptionParser(
1775 usage='Usage: %prog [options] COMMAND',
1776 description='Generate a duk_config.h or config option documentation based on config metadata.',
1777 epilog='COMMAND can be one of: ' + ', '.join(commands) + '.'
1778 )
1779 parser.add_option('--metadata', dest='metadata', default=None, help='metadata directory or metadata tar.gz file')
1780 parser.add_option('--output', dest='output', default=None, help='output filename for C header or RST documentation file')
1781 parser.add_option('--platform', dest='platform', default=None, help='platform (for "barebones-header" command)')
1782 parser.add_option('--compiler', dest='compiler', default=None, help='compiler (for "barebones-header" command)')
1783 parser.add_option('--architecture', dest='architecture', default=None, help='architecture (for "barebones-header" command)')
1784 parser.add_option('--dll', dest='dll', action='store_true', default=False, help='dll build of Duktape, affects symbol visibility macros especially on Windows') # FIXME: unimplemented
1785 parser.add_option('--emit-legacy-feature-check', dest='emit_legacy_feature_check', action='store_true', default=False, help='emit preprocessor checks to reject legacy feature options (DUK_OPT_xxx)')
1786 parser.add_option('--emit-config-sanity-check', dest='emit_config_sanity_check', action='store_true', default=False, help='emit preprocessor checks for config option consistency (DUK_OPT_xxx)')
1787 parser.add_option('--omit-removed-config-options', dest='omit_removed_config_options', action='store_true', default=False, help='omit removed config options from generated headers')
1788 parser.add_option('--omit-deprecated-config-options', dest='omit_deprecated_config_options', action='store_true', default=False, help='omit deprecated config options from generated headers')
1789 parser.add_option('--omit-unused-config-options', dest='omit_unused_config_options', action='store_true', default=False, help='omit unused config options from generated headers')
1790 parser.add_option('--add-active-defines-macro', dest='add_active_defines_macro', action='store_true', default=False, help='add DUK_ACTIVE_DEFINES macro, for development only')
1791 parser.add_option('--define', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_define, default=force_options_yaml, help='force #define option using a C compiler like syntax, e.g. "--define DUK_USE_DEEP_C_STACK" or "--define DUK_USE_TRACEBACK_DEPTH=10"')
1792 parser.add_option('-D', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_define, default=force_options_yaml, help='synonym for --define, e.g. "-DDUK_USE_DEEP_C_STACK" or "-DDUK_USE_TRACEBACK_DEPTH=10"')
1793 parser.add_option('--undefine', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_undefine, default=force_options_yaml, help='force #undef option using a C compiler like syntax, e.g. "--undefine DUK_USE_DEEP_C_STACK"')
1794 parser.add_option('-U', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_undefine, default=force_options_yaml, help='synonym for --undefine, e.g. "-UDUK_USE_DEEP_C_STACK"')
1795 parser.add_option('--option-yaml', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_yaml, default=force_options_yaml, help='force option(s) using inline YAML (e.g. --option-yaml "DUK_USE_DEEP_C_STACK: true")')
1796 parser.add_option('--option-file', type='string', dest='force_options_yaml', action='callback', callback=add_force_option_file, default=force_options_yaml, help='YAML file(s) providing config option overrides')
1797 parser.add_option('--fixup-file', type='string', dest='fixup_header_lines', action='callback', callback=add_fixup_header_file, default=fixup_header_lines, help='C header snippet file(s) to be appended to generated header, useful for manual option fixups')
1798 parser.add_option('--fixup-line', type='string', dest='fixup_header_lines', action='callback', callback=add_fixup_header_line, default=fixup_header_lines, help='C header fixup line to be appended to generated header (e.g. --fixup-line "#define DUK_USE_FASTINT")')
1799 parser.add_option('--sanity-warning', dest='sanity_strict', action='store_false', default=True, help='emit a warning instead of #error for option sanity check issues')
1800 parser.add_option('--use-cpp-warning', dest='use_cpp_warning', action='store_true', default=False, help='emit a (non-portable) #warning when appropriate')
1801 parser.add_option('--git-commit', dest='git_commit', default=None, help='git commit hash to be included in header comments')
1802 parser.add_option('--git-describe', dest='git_describe', default=None, help='git describe string to be included in header comments')
1803 (opts, args) = parser.parse_args()
1804
1805 meta_dir = opts.metadata
1806 if opts.metadata is None:
1807 if os.path.isfile(os.path.join('.', 'genconfig_metadata.tar.gz')):
1808 opts.metadata = 'genconfig_metadata.tar.gz'
1809 elif os.path.isdir(os.path.join('.', 'config-options')):
1810 opts.metadata = '.'
1811
1812 if opts.metadata is not None and os.path.isdir(opts.metadata):
1813 meta_dir = opts.metadata
1814 print 'Using metadata directory: %r' % meta_dir
1815 elif opts.metadata is not None and os.path.isfile(opts.metadata) and tarfile.is_tarfile(opts.metadata):
1816 meta_dir = get_auto_delete_tempdir()
1817 tar = tarfile.open(name=opts.metadata, mode='r:*')
1818 tar.extractall(path=meta_dir)
1819 print 'Using metadata tar file %r, unpacked to directory: %r' % (opts.metadata, meta_dir)
1820 else:
1821 raise Exception('metadata source must be a directory or a tar.gz file')
1822
1823 scan_snippets(os.path.join(meta_dir, 'header-snippets'))
1824 scan_use_defs(os.path.join(meta_dir, 'config-options'))
1825 scan_opt_defs(os.path.join(meta_dir, 'feature-options'))
1826 scan_use_tags()
1827 scan_tags_meta(os.path.join(meta_dir, 'tags.yaml'))
1828 print('Scanned %d DUK_OPT_xxx, %d DUK_USE_XXX, %d helper snippets' % \
1829 (len(opt_defs.keys()), len(use_defs.keys()), len(helper_snippets)))
1830 #print('Tags: %r' % use_tags_list)
1831
1832 if len(args) == 0:
1833 raise Exception('missing command')
1834 cmd = args[0]
1835
1836 if cmd == 'autodetect-header':
1837 cmd = 'autodetect-header-legacy'
1838
1839 if cmd == 'autodetect-header-legacy':
1840 # Generate a duk_config.h similar to Duktape 1.2 feature detection,
1841 # based on manually written monolithic snippets.
1842 # To be replaced by modular header.
1843 result = generate_autodetect_duk_config_header(opts, meta_dir)
1844 with open(opts.output, 'wb') as f:
1845 f.write(result)
1846 elif cmd == 'autodetect-header-modular':
1847 # Generate a duk_config.h similar to Duktape 1.2 feature detection.
1848 # Platform, architecture, and compiler can each be either autodetected
1849 # or specified by user. Generated header is based on modular snippets
1850 # rather than a monolithic platform detection header.
1851 result = generate_autodetect_duk_config_header_modular(opts, meta_dir)
1852 with open(opts.output, 'wb') as f:
1853 f.write(result)
1854 elif cmd == 'barebones-header':
1855 # Generate a duk_config.h with default options for a specific platform,
1856 # compiler, and architecture.
1857 result = generate_barebones_duk_config_header(opts, meta_dir)
1858 with open(opts.output, 'wb') as f:
1859 f.write(result)
1860 elif cmd == 'feature-documentation':
1861 result = generate_feature_option_documentation(opts)
1862 with open(opts.output, 'wb') as f:
1863 f.write(result)
1864 elif cmd == 'config-documentation':
1865 result = generate_config_option_documentation(opts)
1866 with open(opts.output, 'wb') as f:
1867 f.write(result)
1868 else:
1869 raise Exception('invalid command: %r' % cmd)
1870
1871 if __name__ == '__main__':
1872 main()