123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- #!@PYTHON@
- # Copyright (C) 2010-2013 Internet Systems Consortium, Inc. ("ISC")
- #
- # Permission to use, copy, modify, and/or distribute this software for any
- # purpose with or without fee is hereby granted, provided that the above
- # copyright notice and this permission notice appear in all copies.
- #
- # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- # PERFORMANCE OF THIS SOFTWARE.
- """\
- This is a supplemental script to (half) auto-generate DNS Rdata related
- classes and constants.
- """
- import os
- from os.path import getmtime
- import re
- import sys
- re_typecode = re.compile('([\da-z\-]+)_(\d+)')
- classcode2txt = {}
- typecode2txt = {}
- # For meta types and types well-known but not implemented. This is a dict from
- # type code values (as string) to textual mnemonic.
- meta_types = {
- # Real meta types. We won't have Rdata implement for them, but we need
- # RRType constants.
- '251': 'ixfr', '252': 'axfr', '255': 'any',
- # Obsolete types. We probably won't implement Rdata for them, but it's
- # better to have RRType constants.
- '3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt',
- '38': 'a6', '254': 'maila',
- # Types officially assigned but not yet supported in our implementation.
- '10': 'null', '11': 'wks', '19': 'x25', '21': 'rt', '22': 'nsap',
- '23': 'nsap-ptr', '24': 'sig', '20': 'isdn', '25': 'key', '26': 'px',
- '27': 'gpos', '29': 'loc', '36': 'kx', '37': 'cert', '42': 'apl',
- '45': 'ipseckey', '52': 'tlsa', '55': 'hip', '103': 'unspec',
- '104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', '249': 'tkey',
- '253': 'mailb', '256': 'uri'
- }
- # Classes that don't have any known types. This is a dict from type code
- # values (as string) to textual mnemonic.
- meta_classes = {'254': 'none'}
- typeandclass = []
- generic_code = 65536 # something larger than any code value
- rdata_declarations = ''
- class_definitions = ''
- classdir_mtime = 0
- rdatadef_mtime = 0
- rdatahdr_mtime = 0
- heading_txt = '''///////////////
- ///////////////
- /////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py.
- /////////////// DO NOT EDIT!
- ///////////////
- ///////////////
- '''
- def import_classdef(class_txt, file):
- content = ''
- rdata_source = open(file, 'r')
- for line in rdata_source.readlines():
- if re.match('// BEGIN_ISC_NAMESPACE', line):
- content += 'namespace isc {\n'
- content += 'namespace dns {\n'
- continue
- if re.match('// BEGIN_RDATA_NAMESPACE', line):
- content += 'namespace rdata {\n'
- content += 'namespace ' + class_txt + ' {\n'
- continue
- if re.match('// END_ISC_NAMESPACE', line):
- content += '} // end of namespace "dns"\n'
- content += '} // end of namespace "isc"\n'
- continue
- if re.match('// END_RDATA_NAMESPACE', line):
- content += '} // end of namespace "' + class_txt +'"\n'
- content += '} // end of namespace "rdata"\n'
- continue
- content += line
- rdata_source.close()
- return content
- def import_classheader(class_txt, type_txt, type_code, file):
- type_utxt = type_txt.upper()
- class_utxt = class_txt.upper()
- # for each CLASS_n/TYPE_m.h
- rdata_header = open(file, 'r')
- content = ''
- guard_macro = class_txt.upper() + '_' + type_txt.upper()
- guard_macro += '_' + type_code + '_H'
- for line in rdata_header.readlines():
- if re.match('// BEGIN_HEADER_GUARD', line):
- content += '#ifndef ' + guard_macro + '\n'
- content += '#define ' + guard_macro + ' 1\n'
- continue
- if re.match('// END_HEADER_GUARD', line):
- content += '#endif // ' + guard_macro + '\n'
- continue
- if re.match('// BEGIN_ISC_NAMESPACE', line):
- content += 'namespace isc {\n'
- content += 'namespace util {\n'
- content += '''
- class InputBuffer;
- class OutputBuffer;\n'''
- content += '}\n\n'
- content += 'namespace dns {\n'
- continue
- if re.match('// BEGIN_RDATA_NAMESPACE', line):
- content += 'namespace rdata {\n'
- content += 'namespace ' + class_txt + ' {\n'
- continue
- if re.match('// END_ISC_NAMESPACE', line):
- content += '} // end of namespace "dns"\n'
- content += '} // end of namespace "isc"\n'
- continue
- if re.match('// END_RDATA_NAMESPACE', line):
- content += '} // end of namespace "' + class_txt +'"\n'
- content += '} // end of namespace "rdata"\n'
- continue
- if re.match('// Local Variables:', line):
- break
- content += line
- if re.match('// BEGIN_COMMON_DECLARATIONS', line):
- content += '''
- class AbstractMessageRenderer;\n\n'''
- if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
- content += '''
- explicit ''' + type_utxt + '''(const std::string& type_str);
- ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len);
- ''' + type_utxt + '''(const ''' + type_utxt + '''& other);
- ''' + type_utxt + '''(
- MasterLexer& lexer, const Name* name,
- MasterLoader::Options options, MasterLoaderCallbacks& callbacks);
- virtual std::string toText() const;
- virtual void toWire(isc::util::OutputBuffer& buffer) const;
- virtual void toWire(AbstractMessageRenderer& renderer) const;
- virtual int compare(const Rdata& other) const;\n\n'''
- rdata_header.close()
- return content
- def import_definitions(classcode2txt, typecode2txt, typeandclass):
- global rdata_declarations
- global class_definitions
- global classdir_mtime
- global rdatadef_mtime
- global rdatahdr_mtime
- if classdir_mtime < getmtime('@srcdir@/rdata'):
- classdir_mtime = getmtime('@srcdir@/rdata')
- # Sort directories before iterating through them so that the directory
- # list is processed in the same order on all systems. The resulting
- # files should compile regardless of the order in which the components
- # are included but... Having a fixed order for the directories should
- # eliminate system-dependent problems. (Note that the directory names
- # in BIND 10 are ASCII, so the order should be locale-independent.)
- dirlist = os.listdir('@srcdir@/rdata')
- dirlist.sort()
- for dir in dirlist:
- classdir = '@srcdir@/rdata' + os.sep + dir
- m = re_typecode.match(dir)
- if os.path.isdir(classdir) and (m != None or dir == 'generic'):
- if dir == 'generic':
- class_txt = 'generic'
- class_code = generic_code
- else:
- class_txt = m.group(1)
- class_code = m.group(2)
- if not class_code in classcode2txt:
- classcode2txt[class_code] = class_txt
- # Same considerations as directories regarding sorted order
- # also apply to files.
- filelist = os.listdir(classdir)
- filelist.sort()
- for file in filelist:
- file = classdir + os.sep + file
- m = re_typecode.match(os.path.split(file)[1])
- if m != None:
- type_txt = m.group(1)
- type_code = m.group(2)
- if not type_code in typecode2txt:
- typecode2txt[type_code] = type_txt
- if re.search('\.cc$', file):
- if rdatadef_mtime < getmtime(file):
- rdatadef_mtime = getmtime(file)
- class_definitions += import_classdef(class_txt, file)
- elif re.search('\.h$', file):
- if rdatahdr_mtime < getmtime(file):
- rdatahdr_mtime = getmtime(file)
- rdata_declarations += import_classheader(class_txt,
- type_txt,
- type_code,
- file)
- typeandclass.append((type_txt, int(type_code),
- (class_txt, class_txt),
- int(class_code)))
- if class_txt == 'generic':
- typeandclass.append((type_txt, int(type_code),
- (class_txt, 'in'), 1))
- def need_generate(file, mtime):
- '''Check if we need to generate the specified file.
- To avoid unnecessary compilation, we skip (re)generating the file when
- the file already exists and newer than the base file.
- '''
- if os.path.exists(file) and getmtime(file) > mtime:
- return False
- return True
- def generate_rdatadef(file, basemtime):
- if not need_generate(file, basemtime):
- print('skip generating ' + file);
- return
- rdata_deffile = open(file, 'w')
- rdata_deffile.write(heading_txt)
- rdata_deffile.write(class_definitions)
- rdata_deffile.close()
- def generate_rdatahdr(file, heading, declarations, basemtime):
- if not need_generate(file, basemtime):
- print('skip generating ' + file);
- return
- heading += '''
- #ifndef DNS_RDATACLASS_H
- #define DNS_RDATACLASS_H 1
- #include <dns/master_loader.h>
- namespace isc {
- namespace dns {
- class Name;
- class MasterLexer;
- class MasterLoaderCallbacks;
- }
- }
- '''
- declarations += '''
- #endif // DNS_RDATACLASS_H
- // Local Variables:
- // mode: c++
- // End:
- '''
- rdata_header = open(file, 'w')
- rdata_header.write(heading)
- rdata_header.write(declarations)
- rdata_header.close()
- def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class):
- placeholder = '@srcdir@/' + fileprefix + '-placeholder.h'
- outputfile = '@builddir@/' + fileprefix + '.h'
- py_outputfile = '@builddir@/python/' + fileprefix + '_constants_inc.cc'
- upper_key = type_or_class.upper() # TYPE or CLASS
- lower_key = 'rr' + type_or_class.lower() # rrtype or rrclass
- cap_key = type_or_class # Type or Class
- # We only decide whether to generate files for libdns++ files; Python
- # files are generated if and only if libdns++ files are generated.
- # In practice it should be sufficient.
- if (not need_generate(outputfile, basemtime) and
- getmtime(outputfile) > getmtime(placeholder)):
- print('skip generating ' + outputfile)
- return
- # Create a list of (code, code-text) pairs, where code-text is generally
- # upper-cased, with applying speicial filters when necessary.
- def convert(code_txt):
- # Workaround by heuristics: there's a "NULL" RR type, but it would
- # cause conflict with the C/C++ macro. We use Null as a special case.
- if code_txt == 'null':
- return 'Null'
- # Likewise, convert "nsap-ptr" to "NSAP_PTR" as a dash cannot be part
- # of a C/C++ variable.
- if code_txt == 'nsap-ptr':
- return 'NSAP_PTR'
- return code_txt.upper()
- codes = [ (code, convert(txt)) for code, txt in code2txt.items() ]
- # Dump source code for libdns++
- with open(placeholder, 'r') as header_temp:
- with open(outputfile, 'w') as header_out:
- header_out.write(heading_txt)
- for line in header_temp:
- header_out.write(line)
- if re.match('\s+// BEGIN_WELL_KNOWN_' + upper_key +
- '_DECLARATIONS$', line):
- for code in codes:
- header_out.write(' ' * 4 + 'static const RR' +
- cap_key + '& ' + code[1] + '();\n')
- if re.match('// BEGIN_WELL_KNOWN_' + upper_key +
- '_DEFINITIONS$', line):
- for code in codes:
- header_out.write('''inline const RR''' + cap_key +
- '''&
- RR''' + cap_key + '''::''' + code[1] + '''() {
- static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code[0] + ''');
- return (''' + lower_key + ''');
- }\n
- ''')
- # Dump source code snippet for isc.dns Python module
- with open(py_outputfile, 'w') as py_out:
- py_out.write(" // auto-generated by ../gen-rdatacode.py."
- " Don't edit this file.\n")
- py_out.write("\n")
- for code in codes:
- py_out.write('''\
- installClassVariable(''' + lower_key + '''_type, "''' + code[1] + '''",
- createRR''' + cap_key + '''Object(RR''' + \
- cap_key + '''::''' + code[1] + '''()));
- ''')
- def generate_rrparam(fileprefix, basemtime):
- placeholder = '@srcdir@/' + fileprefix + '-placeholder.cc'
- outputfile = '@builddir@/' + fileprefix + '.cc'
- if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder):
- print('skip generating ' + outputfile)
- return
- # sort by class, then by type
- typeandclassparams = ''
- typeandclass.sort(key = lambda x: (x[3], x[1]))
- for param in typeandclass:
- # for rrparamregistry.cc
- # each param is a tuple of (type_txt, type_code, class_tuple,
- # class_code)
- (type_txt, type_code, class_tuple, class_code) = param
- type_utxt = type_txt.upper()
- class_txt = class_tuple[0]
- class_utxt = class_tuple[1].upper()
- indent = ' ' * 8
- typeandclassparams += indent
- if class_tuple[1] != 'generic':
- typeandclassparams += 'add("' + type_utxt + '", '
- typeandclassparams += str(type_code) + ', "' + class_utxt
- typeandclassparams += '", ' + str(class_code)
- typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
- typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
- else:
- typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code)
- typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
- typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
- typeandclassparams += indent + '// Meta and non-implemented RR types\n'
- for type_code, type_txt in meta_types.items():
- typeandclassparams += indent + \
- 'addType("' + type_txt.upper() + '", ' + type_code + ');\n'
- typeandclassparams += indent + '// Meta classes\n'
- for cls_code, cls_txt in meta_classes.items():
- typeandclassparams += indent + \
- 'addClass("' + cls_txt.upper() + '", ' + cls_code + ');\n'
- rrparam_temp = open(placeholder, 'r')
- rrparam_out = open(outputfile, 'w')
- rrparam_out.write(heading_txt)
- for line in rrparam_temp.readlines():
- rrparam_out.write(line)
- if re.match('\s+// BEGIN_WELL_KNOWN_PARAMS', line):
- rrparam_out.write(typeandclassparams)
- rrparam_temp.close()
- rrparam_out.close()
- if __name__ == "__main__":
- try:
- import_definitions(classcode2txt, typecode2txt, typeandclass)
- generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime)
- generate_rdatahdr('@builddir@/rdataclass.h', heading_txt,
- rdata_declarations, rdatahdr_mtime)
- # merge auto-generated types/classes with meta maps and generate the
- # corresponding code.
- generate_typeclasscode('rrtype', rdatahdr_mtime,
- dict(typecode2txt, **meta_types), 'Type')
- generate_typeclasscode('rrclass', classdir_mtime,
- dict(classcode2txt, **meta_classes), 'Class')
- generate_rrparam('rrparamregistry', rdatahdr_mtime)
- except:
- sys.stderr.write('Code generation failed due to exception: %s\n' %
- sys.exc_info()[1])
- exit(1)
|