3 # Process Duktape option metadata and produce various useful outputs:
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)
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.
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.
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
36 # Globals holding scanned metadata, helper snippets, etc
39 # Metadata to scan from config files.
47 required_use_meta_keys
= [
54 allowed_use_meta_keys
= [
59 'related_feature_defines',
71 required_opt_meta_keys
= [
77 allowed_opt_meta_keys
= [
90 # Preferred tag order for option documentation.
102 # Preferred tag order for generated C header files.
103 header_tag_order
= doc_tag_order
105 # Helper headers snippets.
106 helper_snippets
= None
108 # Assume these provides come from outside.
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
115 # Platform files must provide at least these (additional checks
116 # in validate_platform_file()).
117 platform_required_provides
= [
119 'DUK_SETJMP', 'DUK_LONGJMP',
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'
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.
136 'DUK_USE_COMPILER_STRING',
138 'DUK_EXTERNAL_DECL', 'DUK_EXTERNAL',
139 'DUK_INTERNAL_DECL', 'DUK_INTERNAL',
140 'DUK_LOCAL_DECL', 'DUK_LOCAL',
142 'DUK_FILE_MACRO', 'DUK_LINE_MACRO', 'DUK_FUNC_MACRO'
146 # Miscellaneous helpers
149 def get_auto_delete_tempdir():
150 tmpdir
= tempfile
.mkdtemp(suffix
='-genconfig')
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
)
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.
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.
167 # Avoid Python 2.6 vs. Python 2.7 argument differences.
170 return re
.sub(re
.compile('DUK_\w+', re
.MULTILINE
), 'xxx', x
.group(0))
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')
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
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
185 def __init__(self
, lines
, provides
=None, requires
=None, autoscan_requires
=True, autoscan_provides
=True):
187 if not isinstance(lines
, list):
188 raise Exception('Snippet constructor must be a list (not e.g. a string): %s' % repr(lines
))
190 if isinstance(line
, str):
191 self
.lines
.append(line
)
192 elif isinstance(line
, unicode):
193 self
.lines
.append(line
.encode('utf-8'))
195 raise Exception('invalid line: %r' % line
)
197 if provides
is not None:
198 for k
in provides
.keys():
199 self
.provides
[k
] = True
201 if requires
is not None:
202 for k
in requires
.keys():
203 self
.requires
[k
] = True
205 stripped_lines
= strip_comments_from_lines(lines
)
206 # for line in stripped_lines: print(line)
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.)
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.
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
)
232 if len(m
) > 0 and m
[-1] == '_':
233 # Don't allow e.g. DUK_USE_ which results from matching DUK_USE_xxx
235 elif m
[:7] == 'DUK_OPT':
236 # DUK_OPT_xxx always come from outside
238 elif m
[:7] == 'DUK_USE':
239 # DUK_USE_xxx are internal and they should not be 'requirements'
241 elif self
.provides
.has_key(m
):
242 # Snippet provides it's own require; omit
245 #print('REQUIRES: %r' % m)
246 self
.requires
[m
] = True
248 def fromFile(cls
, filename
):
250 with
open(filename
, 'rb') as f
:
255 return Snippet(lines
, autoscan_requires
=True, autoscan_provides
=True)
256 fromFile
= classmethod(fromFile
)
258 def merge(cls
, snippets
):
259 ret
= Snippet([], [], [])
262 for k
in s
.provides
.keys():
263 ret
.provides
[k
] = True
264 for k
in s
.requires
.keys():
265 ret
.requires
[k
] = True
267 merge
= classmethod(merge
)
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
274 vals
= None # snippet list
276 use_cpp_warning
= False
278 def __init__(self
, base_dir
=None, use_cpp_warning
=False):
280 self
.base_dir
= base_dir
281 self
.use_cpp_warning
= use_cpp_warning
283 def line(self
, line
):
284 self
.vals
.append(Snippet([ line
]))
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')))
292 self
.vals
.append(Snippet([ '' ]))
294 def rst_heading(self
, title
, char
, doubled
=False):
297 tmp
.append(char
* len(title
))
299 tmp
.append(char
* len(title
))
300 self
.vals
.append(Snippet(tmp
))
302 def snippet_relative(self
, fn
):
303 sn
= Snippet
.fromFile(os
.path
.join(self
.base_dir
, fn
))
306 def snippet_absolute(fn
):
307 sn
= Snippet
.fromFile(fn
)
310 def cpp_error(self
, msg
):
311 # XXX: assume no newlines etc
312 self
.vals
.append(Snippet([ '#error %s' % msg
]))
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
]))
321 self
.vals
.append(Snippet([ '/* WARNING: %s */' % msg
]))
323 def cpp_warning_or_error(self
, msg
, is_error
=True):
327 self
.cpp_warning(msg
)
329 def chdr_block_heading(self
, msg
):
333 lines
.append(' * ' + msg
)
336 self
.vals
.append(Snippet(lines
))
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
345 return '\n'.join(tmp
)
347 def fill_dependencies_for_snippets(self
, idx_deps
):
348 fill_dependencies_for_snippets(self
.vals
, idx_deps
)
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).
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.
362 resolved
= [] # for printing only
366 return # already present
371 for k
in sn
.requires
.keys():
372 if assumed_provides
.has_key(k
):
377 if sn2
.provides
.has_key(k
):
378 if not graph
.has_key(sn
):
380 graph
[sn
].append(sn2
)
381 found
= True # at least one other node provides 'k'
384 #print 'Resolving %r' % k
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.
392 for sn2
in helper_snippets
:
393 if sn2
.provides
.has_key(k
):
397 print(repr(sn
.lines
))
398 raise Exception('cannot resolve missing require: %r' % k
)
400 # Snippet may have further unresolved provides; add recursively
401 to_add
.append(sn_req
)
403 if not graph
.has_key(sn
):
405 graph
[sn
].append(sn_req
)
410 # Add original snippets. This fills in the required nodes
415 # Figure out fill-ins by looking for snippets not in original
416 # list and without any unserialized dependent nodes.
424 if handled
.has_key(sn
):
428 for dep
in graph
.get(sn
, []):
429 if not handled
.has_key(dep
):
432 snippets
.insert(idx_deps
, sn
)
434 snippets
.insert(idx_deps
, Snippet([ '' ]))
440 # XXX: detect and handle loops cleanly
442 if handled
.has_key(sn
):
444 print('UNHANDLED KEY')
445 print('PROVIDES: %r' % sn
.provides
)
446 print('REQUIRES: %r' % sn
.requires
)
447 print('\n'.join(sn
.lines
))
450 # print(repr(snlist))
451 print 'Resolved helper defines: %r' % resolved
453 def serialize_snippet_list(snippets
):
456 emitted_provides
= {}
457 for k
in assumed_provides
.keys():
458 emitted_provides
[k
] = True
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)
470 return '\n'.join(ret
)
472 def remove_duplicate_newlines(x
):
475 for line
in x
.split('\n'):
485 return '\n'.join(ret
)
487 def scan_use_defs(dirname
):
488 global use_defs
, use_defs_list
492 for fn
in os
.listdir(dirname
):
493 root
, ext
= os
.path
.splitext(fn
)
494 if not root
.startswith('DUK_USE_') or ext
!= '.yaml':
496 with
open(os
.path
.join(dirname
, fn
), 'rb') as f
:
498 if doc
.get('example', False):
500 if doc
.get('unimplemented', False):
501 print('WARNING: unimplemented: %s' % fn
)
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
:
509 print('WARNING: missing key %s in metadata file %s' % (k
, fn
))
511 use_defs
[doc
['define']] = doc
513 keys
= use_defs
.keys()
516 use_defs_list
.append(use_defs
[k
])
518 def scan_opt_defs(dirname
):
519 global opt_defs
, opt_defs_list
523 for fn
in os
.listdir(dirname
):
524 root
, ext
= os
.path
.splitext(fn
)
525 if not root
.startswith('DUK_OPT_') or ext
!= '.yaml':
527 with
open(os
.path
.join(dirname
, fn
), 'rb') as f
:
529 if doc
.get('example', False):
531 if doc
.get('unimplemented', False):
532 print('WARNING: unimplemented: %s' % fn
)
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
:
540 print('WARNING: missing key %s in metadata file %s' % (k
, fn
))
542 opt_defs
[doc
['define']] = doc
544 keys
= opt_defs
.keys()
547 opt_defs_list
.append(opt_defs
[k
])
550 global use_tags
, use_tags_list
553 for doc
in use_defs_list
:
554 for tag
in doc
.get('tags', []):
557 use_tags_list
= use_tags
.keys()
560 def scan_tags_meta(filename
):
563 with
open(filename
, 'rb') as f
:
564 tags_meta
= yaml
.load(f
)
566 def scan_snippets(dirname
):
567 global helper_snippets
570 for fn
in os
.listdir(dirname
):
571 if (fn
[0:6] != 'DUK_F_'):
573 #print('Autoscanning snippet: %s' % fn)
574 helper_snippets
.append(Snippet
.fromFile(os
.path
.join(dirname
, fn
)))
576 def validate_platform_file(filename
):
577 sn
= Snippet
.fromFile(filename
)
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
))
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
)
588 def validate_architecture_file(filename
):
589 sn
= Snippet
.fromFile(filename
)
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
))
596 def validate_compiler_file(filename
):
597 sn
= Snippet
.fromFile(filename
)
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
))
604 def get_tag_title(tag
):
605 meta
= tags_meta
.get(tag
, None)
609 return meta
.get('title', tag
)
611 def get_tag_description(tag
):
612 meta
= tags_meta
.get(tag
, None)
616 return meta
.get('description', None)
618 def get_tag_list_with_preferred_order(preferred
):
621 # Preferred tags first
622 for tag
in preferred
:
626 # Remaining tags in alphabetic order
627 for tag
in use_tags_list
:
631 #print('Effective tag order: %r' % tags)
634 def rst_format(text
):
635 # XXX: placeholder, need to decide on markup conventions for YAML files
637 for para
in text
.split('\n'):
641 return '\n\n'.join(ret
)
644 if not isinstance(x
, (int, long)):
645 raise Exception('invalid input: %r' % x
)
647 # XXX: unsigned constants?
648 if x
> 0x7fffffff or x
< -0x80000000:
650 elif x
> 0x7fff or x
< -0x8000:
656 if isinstance(x
, unicode):
657 x
= x
.encode('utf-8')
658 if not isinstance(x
, str):
659 raise Exception('invalid input: %r' % x
)
666 # Avoid ambiguous hex escapes
671 if o
< 0x20 or o
> 0x7e or c
in '"\\':
679 res
= '(' + res
+ ')'
684 # Autogeneration of option documentation
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
)
692 tags
= get_tag_list_with_preferred_order(doc_tag_order
)
695 ret
.rst_heading(title
, '=', doubled
=True)
703 if tag
!= doc
['tags'][0]: # sort under primary tag
705 dname
= doc
['define']
706 desc
= doc
.get('description', None)
708 if handled
.has_key(dname
):
709 raise Exception('define handled twice, should not happen: %r' % dname
)
710 handled
[dname
] = True
712 if first
: # emit tag heading only if there are subsections
714 ret
.rst_heading(get_tag_title(tag
), '=')
716 tag_desc
= get_tag_description(tag
)
717 if tag_desc
is not None:
719 ret
.line(rst_format(tag_desc
))
723 ret
.rst_heading(dname
, '-')
727 ret
.line(rst_format(desc
))
731 ret
.line('Default: ``' + str(doc
['default']) + '``') # XXX: rst or other format
734 dname
= doc
['define']
735 if not handled
.has_key(dname
):
736 raise Exception('unhandled define (maybe missing from tags list?): %r' % dname
)
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)
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)
748 # Helpers for duk_config.h generation
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).
755 for val
in opts
.force_options_yaml
:
756 doc
= yaml
.load(StringIO
.StringIO(val
))
758 if use_defs
.has_key(k
):
761 print 'WARNING: option override key %s not defined in metadata, ignoring' % k
762 forced_opts
[k
] = doc
[k
] # shallow copy
764 print 'Overrides: %s' % json
.dumps(forced_opts
)
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'])
775 ret
.line('#define ' + defname
)
776 elif defval
== False:
778 ret
.line('#undef ' + defname
)
780 # Default value is false, and caller has emitted
781 # an unconditional #undef, so don't emit a duplicate
783 elif isinstance(defval
, (int, long)):
785 ret
.line('#define ' + defname
+ ' ' + cint_encode(defval
))
786 elif isinstance(defval
, (str, unicode)):
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'):
795 ret
.line('#define ' + defname
+ ' ' + cstr_encode(defval
['string']))
797 raise Exception('unsupported value for option %s: %r' % (defname
, defval
))
799 raise Exception('unsupported value for option %s: %r' % (defname
, defval
))
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)')
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
:
818 for doc
in use_defs_list
:
819 if optname
in doc
.get('related_feature_defines', []):
820 suggested
.append(doc
['define'])
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
)
826 ret
.cpp_warning_or_error('unsupported legacy feature option %s used' % optname
, opts
.sanity_strict
)
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)')
837 for doc
in use_defs_list
:
838 if doc
['define'] not in defs
:
839 defs
.append(doc
['define'])
843 doc
= use_defs
[optname
]
844 dname
= doc
['define']
848 if doc
.get('removed', None) is not None:
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
)
853 elif doc
.get('deprecated', None) is not None:
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
)
859 for req
in doc
.get('requires', []):
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
)
865 for req
in doc
.get('conflicts', []):
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
)
873 # Add a header snippet for providing a __OVERRIDE_DEFINES__ section.
874 def add_override_defines_section(opts
, ret
):
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.')
882 ret
.line('/* __OVERRIDE_DEFINES__ */')
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')
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.
895 config_define
= doc
['define']
897 feature_define
= None
898 feature_no_define
= None
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']
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
)
912 ret
.line('#undef %s' % config_define
)
914 ret
.line('#define %s' % config_define
)
915 ret
.line('#elif defined(%s)' % feature_no_define
)
917 ret
.line('#define %s' % config_define
)
919 ret
.line('#undef %s' % config_define
)
922 emit_default_from_config_meta(ret
, doc
, forced_opts
, undef_done
)
924 elif doc
.has_key('feature_snippet'):
925 ret
.lines(doc
['feature_snippet'])
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)')
942 for doc
in use_defs_list
:
943 defname
= doc
['define']
945 ret
.line('#if defined(%s)' % defname
)
946 ret
.line('#define DUK_ACTIVE_DEF%d " %s"' % (idx
, defname
))
948 ret
.line('#define DUK_ACTIVE_DEF%d ""' % idx
)
954 for i
in xrange(idx
):
955 tmp
.append('DUK_ACTIVE_DEF%d' % i
)
957 ret
.line('#define DUK_ACTIVE_DEFINES ("Active: ["' + ' '.join(tmp
) + ' " ]")')
960 # duk_config.h generation
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
)
974 forced_opts
= get_forced_options(opts
)
976 ret
.snippet_relative('comment_prologue.h.in')
979 ret
.line('#ifndef DUK_CONFIG_H_INCLUDED')
980 ret
.line('#define DUK_CONFIG_H_INCLUDED')
983 # Compiler features, processor/architecture, OS, compiler
984 ret
.snippet_relative('compiler_features.h.in')
986 ret
.snippet_relative('rdtsc.h.in') # XXX: move downwards
988 ret
.snippet_relative('platform1.h.in')
991 # Feature selection, system include, Date provider
992 # Most #include statements are here
993 ret
.snippet_relative('platform2.h.in')
995 ret
.snippet_relative('ullconsts.h.in')
997 ret
.snippet_relative('libc.h.in')
1001 ret
.snippet_relative('types1.h.in')
1002 ret
.line('#if defined(DUK_F_HAVE_INTTYPES)')
1003 ret
.line('/* C99 or compatible */')
1005 ret
.snippet_relative('types_c99.h.in')
1007 ret
.line('#else /* C99 types */')
1009 ret
.snippet_relative('types_legacy.h.in')
1011 ret
.line('#endif /* C99 types */')
1013 ret
.snippet_relative('types2.h.in')
1015 ret
.snippet_relative('64bitops.h.in')
1019 ret
.snippet_relative('alignment.h.in')
1023 ret
.snippet_relative('object_layout.h.in')
1027 ret
.snippet_relative('byteorder.h.in')
1031 ret
.snippet_relative('packed_tval.h.in')
1034 # Detect 'fast math'
1035 ret
.snippet_relative('reject_fast_math.h.in')
1038 # IEEE double constants
1039 ret
.snippet_relative('double_const.h.in')
1042 # Math and other ANSI replacements, NetBSD workaround, paranoid math, paranoid Date
1043 ret
.snippet_relative('repl_math.h.in')
1045 ret
.snippet_relative('paranoid_date.h.in')
1047 ret
.snippet_relative('repl_ansi.h.in')
1050 # Platform function pointers
1051 ret
.snippet_relative('platform_funcptr.h.in')
1054 # General compiler stuff
1055 ret
.snippet_relative('stringify.h.in')
1057 ret
.snippet_relative('segfault.h.in')
1059 ret
.snippet_relative('unreferenced.h.in')
1061 ret
.snippet_relative('noreturn.h.in')
1063 ret
.snippet_relative('unreachable.h.in')
1065 ret
.snippet_relative('likely.h.in')
1067 ret
.snippet_relative('inline.h.in')
1069 ret
.snippet_relative('visibility.h.in')
1071 ret
.snippet_relative('file_line_func.h.in')
1073 ret
.snippet_relative('byteswap.h.in')
1076 # Arhitecture, OS, and compiler strings
1077 ret
.snippet_relative('arch_string.h.in')
1079 ret
.snippet_relative('os_string.h.in')
1081 ret
.snippet_relative('compiler_string.h.in')
1085 ret
.snippet_relative('target_info.h.in')
1089 ret
.snippet_relative('longjmp.h.in')
1092 # Unsorted flags, contains almost all actual Duktape-specific
1093 # but platform independent features
1094 ret
.snippet_relative('unsorted_flags.h.in')
1098 ret
.snippet_relative('user_declare.h.in')
1101 # Emit forced options. If a corresponding option is already defined
1102 # by a snippet above, #undef it first.
1104 tmp
= Snippet(ret
.join().split('\n'))
1106 for doc
in use_defs_list
:
1107 defname
= doc
['define']
1109 if doc
.get('removed', None) is not None and opts
.omit_removed_config_options
:
1111 if doc
.get('deprecated', None) is not None and opts
.omit_deprecated_config_options
:
1113 if doc
.get('unused', False) == True and opts
.omit_unused_config_options
:
1115 if not forced_opts
.has_key(defname
):
1118 if not doc
.has_key('default'):
1119 raise Exception('config option %s is missing default value' % defname
)
1122 ret
.chdr_block_heading('Forced options')
1123 first_forced
= False
1126 if tmp
.provides
.has_key(defname
):
1127 ret
.line('#undef ' + defname
)
1130 emit_default_from_config_meta(ret
, doc
, forced_opts
, undef_done
)
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).
1139 tmp
= Snippet(ret
.join().split('\n'))
1141 for doc
in use_defs_list
:
1142 if doc
.get('removed', None) is not None: # XXX: check version
1144 need
[doc
['define']] = True
1145 for k
in tmp
.provides
.keys():
1148 need_keys
= sorted(need
.keys())
1150 if len(need_keys
) > 0:
1151 ret
.chdr_block_heading('Autogenerated defaults')
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)
1159 ret
.snippet_relative('custom_header.h.in')
1162 if len(opts
.fixup_header_lines
) > 0:
1163 ret
.chdr_block_heading('Fixups')
1164 for line
in opts
.fixup_header_lines
:
1168 add_override_defines_section(opts
, ret
)
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
1173 ret
.snippet_relative('date_provider.h.in')
1177 # XXX: use autogenerated sanity checks instead
1178 ret
.snippet_relative('sanity.h.in')
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
)
1190 ret
.line('#endif /* DUK_CONFIG_H_INCLUDED */')
1191 ret
.empty() # for trailing newline
1192 return remove_duplicate_newlines(ret
.join())
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.
1199 # This approach will replace the legacy autodetect header in Duktape 1.4,
1200 # and most likely the separate barebones header also.
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
)
1210 forced_opts
= get_forced_options(opts
)
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
)
1219 with
open(os
.path
.join(meta_dir
, 'compilers.yaml'), 'rb') as f
:
1220 compilers
= yaml
.load(f
)
1223 ret
.line(' * duk_config.h autodetect header generated by genconfig.py.')
1225 ret
.line(' * Git commit: %s' % opts
.git_commit
or 'n/a')
1226 ret
.line(' * Git describe: %s' % opts
.git_describe
or 'n/a')
1228 if opts
.platform
is not None:
1229 ret
.line(' * Platform: ' + opts
.platform
)
1231 ret
.line(' * Supported platforms:')
1232 for platf
in platforms
['autodetect']:
1233 ret
.line(' * - %s' % platf
.get('name', platf
.get('check')))
1235 if opts
.architecture
is not None:
1236 ret
.line(' * Architecture: ' + opts
.architecture
)
1238 ret
.line(' * Supported architectures:')
1239 for arch
in architectures
['autodetect']:
1240 ret
.line(' * - %s' % arch
.get('name', arch
.get('check')))
1242 if opts
.compiler
is not None:
1243 ret
.line(' * Compiler: ' + opts
.compiler
)
1245 ret
.line(' * Supported compilers:')
1246 for comp
in compilers
['autodetect']:
1247 ret
.line(' * - %s' % comp
.get('name', comp
.get('check')))
1251 ret
.line('#ifndef DUK_CONFIG_H_INCLUDED')
1252 ret
.line('#define DUK_CONFIG_H_INCLUDED')
1255 ret
.chdr_block_heading('Intermediate helper defines')
1257 idx_deps
= len(ret
.vals
) # position where to emit dependencies
1259 # Feature selection, system include, Date provider
1260 # Most #include statements are here
1262 if opts
.platform
is not None:
1263 ret
.chdr_block_heading('Platform: ' + opts
.platform
)
1265 ret
.snippet_relative('platform_cppextras.h.in')
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
)
1273 ret
.chdr_block_heading('Platform autodetection')
1275 ret
.snippet_relative('platform_cppextras.h.in')
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
))
1284 ret
.line('#if defined(%s)' % check
)
1289 ret
.line('#elif defined(%s)' % check
)
1290 ret
.snippet_relative(include
)
1291 ret
.line('#endif /* autodetect platform */')
1293 ret
.snippet_relative('platform_sharedincludes.h.in')
1296 if opts
.architecture
is not None:
1297 ret
.chdr_block_heading('Architecture: ' + opts
.architecture
)
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
)
1304 ret
.chdr_block_heading('Architecture autodetection')
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
))
1312 ret
.line('#if defined(%s)' % check
)
1317 ret
.line('#elif defined(%s)' % check
)
1318 ret
.snippet_relative(include
)
1319 ret
.line('#endif /* autodetect architecture */')
1321 if opts
.compiler
is not None:
1322 ret
.chdr_block_heading('Compiler: ' + opts
.compiler
)
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
)
1329 ret
.chdr_block_heading('Compiler autodetection')
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
))
1337 ret
.line('#if defined(%s)' % check
)
1342 ret
.line('#elif defined(%s)' % check
)
1343 ret
.snippet_relative(include
)
1344 ret
.line('#endif /* autodetect compiler */')
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
1354 # FIXME: __uclibc__ needs stdlib.h, but it really is the only exception
1355 ret
.snippet_relative('libc.h.in')
1359 ret
.snippet_relative('types1.h.in')
1360 ret
.line('#if defined(DUK_F_HAVE_INTTYPES)')
1361 ret
.line('/* C99 or compatible */')
1363 ret
.snippet_relative('types_c99.h.in')
1365 ret
.line('#else /* C99 types */')
1367 ret
.snippet_relative('types_legacy.h.in')
1369 ret
.line('#endif /* C99 types */')
1371 ret
.snippet_relative('types2.h.in')
1373 ret
.snippet_relative('64bitops.h.in')
1377 ret
.snippet_relative('alignment.h.in')
1381 ret
.snippet_relative('object_layout.h.in')
1385 # FIXME: from the architecture snippet
1386 ret
.snippet_relative('byteorder.h.in')
1390 # FIXME: from the architecture snippet
1391 ret
.snippet_relative('packed_tval.h.in')
1394 # Detect 'fast math'
1395 ret
.snippet_relative('reject_fast_math.h.in')
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')
1404 # Math and other ANSI replacements, NetBSD workaround, paranoid math, paranoid Date
1405 ret
.snippet_relative('repl_math.h.in')
1407 ret
.snippet_relative('paranoid_date.h.in')
1409 ret
.snippet_relative('repl_ansi.h.in')
1412 # Platform function pointers
1413 ret
.snippet_relative('platform_funcptr.h.in')
1416 # General compiler stuff
1417 ret
.snippet_relative('stringify.h.in')
1419 ret
.snippet_relative('segfault.h.in')
1421 ret
.snippet_relative('unreferenced.h.in')
1423 ret
.snippet_relative('noreturn.h.in')
1425 ret
.snippet_relative('unreachable.h.in')
1427 ret
.snippet_relative('likely.h.in')
1429 ret
.snippet_relative('inline.h.in')
1431 ret
.snippet_relative('visibility.h.in')
1433 ret
.snippet_relative('file_line_func.h.in')
1435 ret
.snippet_relative('byteswap.h.in')
1438 # These come directly from platform, architecture, and compiler
1440 #ret.snippet_relative('arch_string.h.in')
1442 #ret.snippet_relative('os_string.h.in')
1444 #ret.snippet_relative('compiler_string.h.in')
1446 #ret.snippet_relative('longjmp.h.in')
1450 ret
.snippet_relative('target_info.h.in')
1453 # Automatic DUK_OPT_xxx feature option handling
1454 # FIXME: platform setjmp/longjmp defines now conflict with this
1456 # Unsorted flags, contains almost all actual Duktape-specific
1457 # but platform independent features
1458 #ret.snippet_relative('unsorted_flags.h.in')
1461 add_feature_option_handling(opts
, ret
, forced_opts
)
1463 ret
.snippet_relative('user_declare.h.in')
1466 # Emit forced options. If a corresponding option is already defined
1467 # by a snippet above, #undef it first.
1469 tmp
= Snippet(ret
.join().split('\n'))
1471 for doc
in use_defs_list
:
1472 defname
= doc
['define']
1474 if doc
.get('removed', None) is not None and opts
.omit_removed_config_options
:
1476 if doc
.get('deprecated', None) is not None and opts
.omit_deprecated_config_options
:
1478 if doc
.get('unused', False) == True and opts
.omit_unused_config_options
:
1480 if not forced_opts
.has_key(defname
):
1483 if not doc
.has_key('default'):
1484 raise Exception('config option %s is missing default value' % defname
)
1487 ret
.chdr_block_heading('Forced options')
1488 first_forced
= False
1491 if tmp
.provides
.has_key(defname
):
1492 ret
.line('#undef ' + defname
)
1495 emit_default_from_config_meta(ret
, doc
, forced_opts
, undef_done
)
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).
1504 tmp
= Snippet(ret
.join().split('\n'))
1506 for doc
in use_defs_list
:
1507 if doc
.get('removed', None) is not None: # XXX: check version
1509 need
[doc
['define']] = True
1510 for k
in tmp
.provides
.keys():
1513 need_keys
= sorted(need
.keys())
1515 if len(need_keys
) > 0:
1516 ret
.chdr_block_heading('Autogenerated defaults')
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)
1524 ret
.snippet_relative('custom_header.h.in')
1527 if len(opts
.fixup_header_lines
) > 0:
1528 ret
.chdr_block_heading('Fixups')
1529 for line
in opts
.fixup_header_lines
:
1533 add_override_defines_section(opts
, ret
)
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
1538 ret
.snippet_relative('date_provider.h.in')
1541 ret
.fill_dependencies_for_snippets(idx_deps
)
1543 # FIXME: use autogenerated sanity instead of sanity.h.in
1545 ret
.snippet_relative('sanity.h.in')
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
)
1556 ret
.line('#endif /* DUK_CONFIG_H_INCLUDED */')
1557 ret
.empty() # for trailing newline
1558 return remove_duplicate_newlines(ret
.join())
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.
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
)
1571 # XXX: Provide more defines from YAML config files so that such
1572 # defines can be overridden more conveniently (e.g. DUK_COS).
1574 forced_opts
= get_forced_options(opts
)
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
)
1582 ret
.line(' * Git commit: %s' % opts
.git_commit
or 'n/a')
1583 ret
.line(' * Git describe: %s' % opts
.git_describe
or 'n/a')
1586 ret
.line('#ifndef DUK_CONFIG_H_INCLUDED')
1587 ret
.line('#define DUK_CONFIG_H_INCLUDED')
1589 ret
.chdr_block_heading('Intermediate helper defines')
1591 idx_deps
= len(ret
.vals
) # position where to emit dependencies
1593 ret
.chdr_block_heading('Platform headers and typedefs')
1595 if opts
.platform
is None:
1596 raise Exception('no platform specified')
1598 fn
= 'platform_%s.h.in' % opts
.platform
1599 ret
.snippet_relative(fn
)
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
1604 ret
.chdr_block_heading('Platform features')
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
1611 ret
.snippet_relative('platform_generic.h.in')
1613 ret
.chdr_block_heading('Compiler features')
1615 if opts
.compiler
is None:
1616 raise Exception('no compiler specified')
1618 fn
= 'compiler_%s.h.in' % opts
.compiler
1619 ret
.snippet_relative(fn
)
1621 # noreturn, vacopy, etc
1622 # visibility attributes
1624 ret
.chdr_block_heading('Architecture features')
1626 if opts
.architecture
is None:
1627 raise Exception('no architecture specified')
1629 fn
= 'architecture_%s.h.in' % opts
.architecture
1630 ret
.snippet_relative(fn
)
1632 ret
.chdr_block_heading('Config options')
1634 tags
= get_tag_list_with_preferred_order(header_tag_order
)
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).
1644 for k
in sn
.provides
.keys():
1648 ret
.line('/* ' + get_tag_title(tag
) + ' */')
1650 for doc
in use_defs_list
:
1651 defname
= doc
['define']
1653 if doc
.get('removed', None) is not None and opts
.omit_removed_config_options
:
1655 if doc
.get('deprecated', None) is not None and opts
.omit_deprecated_config_options
:
1657 if doc
.get('unused', False) == True and opts
.omit_unused_config_options
:
1660 if tag
!= doc
['tags'][0]: # sort under primary tag
1663 if not doc
.has_key('default'):
1664 raise Exception('config option %s is missing default value' % defname
)
1668 if handled
.has_key(defname
):
1669 defval
= forced_opts
.get(defname
, None)
1671 ret
.line('/* %s already emitted above */' % defname
)
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
)
1681 # FIXME: macro args; DUK_USE_USER_DECLARE vs. DUK_USE_USER_DECLARE()
1682 # vs. DUK_USE_USER_DECLARE(x,y)
1684 handled
[defname
] = True
1685 emit_default_from_config_meta(ret
, doc
, forced_opts
, undef_done
)
1689 if len(opts
.fixup_header_lines
) > 0:
1690 ret
.chdr_block_heading('Fixups')
1691 for line
in opts
.fixup_header_lines
:
1694 add_override_defines_section(opts
, ret
)
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
1700 ret
.snippet_relative('date_provider.h.in')
1703 ret
.fill_dependencies_for_snippets(idx_deps
)
1705 # XXX: ensure no define is unhandled at the end
1707 # Check for presence of legacy feature options (DUK_OPT_xxx),
1708 # and consistency of final DUK_USE_xxx options.
1710 # These could also be emitted into Duktape source code, but it's
1711 # probably better that the checks can be easily disabled from
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
)
1721 ret
.line('#endif /* DUK_CONFIG_H_INCLUDED */')
1722 ret
.empty() # for trailing newline
1724 return remove_duplicate_newlines(serialize_snippet_list(ret
.vals
)) # XXX: refactor into FileBuilder
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('=')
1744 doc
= { tmp
[0]: True }
1746 doc
= { tmp
[0]: tmp
[1] }
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('=')
1753 doc
= { tmp
[0]: False }
1755 raise Exception('invalid option value: %r' % value
)
1756 force_options_yaml
.append(yaml
.safe_dump(doc
))
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
:
1764 if line
[-1] == '\n':
1766 fixup_header_lines
.append(line
)
1769 'autodetect-header',
1771 'feature-documentation',
1772 'config-documentation'
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
) + '.'
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()
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')):
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
)
1821 raise Exception('metadata source must be a directory or a tar.gz file')
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'))
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)
1833 raise Exception('missing command')
1836 if cmd
== 'autodetect-header':
1837 cmd
= 'autodetect-header-legacy'
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
:
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
:
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
:
1860 elif cmd
== 'feature-documentation':
1861 result
= generate_feature_option_documentation(opts
)
1862 with
open(opts
.output
, 'wb') as f
:
1864 elif cmd
== 'config-documentation':
1865 result
= generate_config_option_documentation(opts
)
1866 with
open(opts
.output
, 'wb') as f
:
1869 raise Exception('invalid command: %r' % cmd
)
1871 if __name__
== '__main__':