]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
7c673cae
FG
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
23import os
24import sys
25import re
26import json
27import yaml
28import optparse
29import tarfile
30import tempfile
31import atexit
32import shutil
33import StringIO
34
35#
36# Globals holding scanned metadata, helper snippets, etc
37#
38
39# Metadata to scan from config files.
40use_defs = None
41use_defs_list = None
42opt_defs = None
43opt_defs_list = None
44use_tags = None
45use_tags_list = None
46tags_meta = None
47required_use_meta_keys = [
48 'define',
49 'introduced',
50 'default',
51 'tags',
52 'description'
53]
54allowed_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]
71required_opt_meta_keys = [
72 'define',
73 'introduced',
74 'tags',
75 'description'
76]
77allowed_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.
91doc_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.
103header_tag_order = doc_tag_order
104
105# Helper headers snippets.
106helper_snippets = None
107
108# Assume these provides come from outside.
109assumed_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()).
117platform_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()).
124architecture_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()).
132compiler_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
149def 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
158def 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.
178re_line_provides = re.compile(r'^#(?:define|undef)\s+(\w+).*$')
179re_line_requires = re.compile(r'(DUK_[A-Z0-9_]+)') # uppercase only, don't match DUK_USE_xxx for example
180class 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.
273class 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
358def 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
453def 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
472def 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
487def 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
518def 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
549def 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
560def scan_tags_meta(filename):
561 global tags_meta
562
563 with open(filename, 'rb') as f:
564 tags_meta = yaml.load(f)
565
566def 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
576def 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
588def 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
596def 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
604def 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
611def 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
618def 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
634def 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
643def 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
655def 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
689def 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
741def 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
744def 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
751def 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).
770def 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.
803def 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.
833def 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.
874def 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.
887def 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.
938def 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.
970def 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.
1206def 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().
1567def 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
1730def 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
1871if __name__ == '__main__':
1872 main()