gen-rdatacode.py.in 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. #!@PYTHON@
  2. # Copyright (C) 2010-2013 Internet Systems Consortium, Inc. ("ISC")
  3. #
  4. # Permission to use, copy, modify, and/or distribute this software for any
  5. # purpose with or without fee is hereby granted, provided that the above
  6. # copyright notice and this permission notice appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  9. # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  10. # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  12. # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  13. # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  14. # PERFORMANCE OF THIS SOFTWARE.
  15. """\
  16. This is a supplemental script to (half) auto-generate DNS Rdata related
  17. classes and constants.
  18. """
  19. import os
  20. from os.path import getmtime
  21. import re
  22. import sys
  23. re_typecode = re.compile('([\da-z\-]+)_(\d+)')
  24. classcode2txt = {}
  25. typecode2txt = {}
  26. # For meta types and types well-known but not implemented. This is a dict from
  27. # type code values (as string) to textual mnemonic.
  28. meta_types = {
  29. # Real meta types. We won't have Rdata implement for them, but we need
  30. # RRType constants.
  31. '251': 'ixfr', '252': 'axfr', '255': 'any',
  32. # Obsolete types. We probably won't implement Rdata for them, but it's
  33. # better to have RRType constants.
  34. '3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt',
  35. '38': 'a6', '254': 'maila',
  36. # Types officially assigned but not yet supported in our implementation.
  37. '10': 'null', '11': 'wks', '19': 'x25', '21': 'rt', '22': 'nsap',
  38. '23': 'nsap-ptr', '24': 'sig', '20': 'isdn', '25': 'key', '26': 'px',
  39. '27': 'gpos', '29': 'loc', '36': 'kx', '37': 'cert', '42': 'apl',
  40. '45': 'ipseckey', '52': 'tlsa', '55': 'hip', '103': 'unspec',
  41. '104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', '249': 'tkey',
  42. '253': 'mailb', '256': 'uri'
  43. }
  44. # Classes that don't have any known types. This is a dict from type code
  45. # values (as string) to textual mnemonic.
  46. meta_classes = {'254': 'none'}
  47. typeandclass = []
  48. generic_code = 65536 # something larger than any code value
  49. rdata_declarations = ''
  50. class_definitions = ''
  51. classdir_mtime = 0
  52. rdatadef_mtime = 0
  53. rdatahdr_mtime = 0
  54. heading_txt = '''///////////////
  55. ///////////////
  56. /////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py.
  57. /////////////// DO NOT EDIT!
  58. ///////////////
  59. ///////////////
  60. '''
  61. def import_classdef(class_txt, file):
  62. content = ''
  63. rdata_source = open(file, 'r')
  64. for line in rdata_source.readlines():
  65. if re.match('// BEGIN_ISC_NAMESPACE', line):
  66. content += 'namespace isc {\n'
  67. content += 'namespace dns {\n'
  68. continue
  69. if re.match('// BEGIN_RDATA_NAMESPACE', line):
  70. content += 'namespace rdata {\n'
  71. content += 'namespace ' + class_txt + ' {\n'
  72. continue
  73. if re.match('// END_ISC_NAMESPACE', line):
  74. content += '} // end of namespace "dns"\n'
  75. content += '} // end of namespace "isc"\n'
  76. continue
  77. if re.match('// END_RDATA_NAMESPACE', line):
  78. content += '} // end of namespace "' + class_txt +'"\n'
  79. content += '} // end of namespace "rdata"\n'
  80. continue
  81. content += line
  82. rdata_source.close()
  83. return content
  84. def import_classheader(class_txt, type_txt, type_code, file):
  85. type_utxt = type_txt.upper()
  86. class_utxt = class_txt.upper()
  87. # for each CLASS_n/TYPE_m.h
  88. rdata_header = open(file, 'r')
  89. content = ''
  90. guard_macro = class_txt.upper() + '_' + type_txt.upper()
  91. guard_macro += '_' + type_code + '_H'
  92. for line in rdata_header.readlines():
  93. if re.match('// BEGIN_HEADER_GUARD', line):
  94. content += '#ifndef ' + guard_macro + '\n'
  95. content += '#define ' + guard_macro + ' 1\n'
  96. continue
  97. if re.match('// END_HEADER_GUARD', line):
  98. content += '#endif // ' + guard_macro + '\n'
  99. continue
  100. if re.match('// BEGIN_ISC_NAMESPACE', line):
  101. content += 'namespace isc {\n'
  102. content += 'namespace util {\n'
  103. content += '''
  104. class InputBuffer;
  105. class OutputBuffer;\n'''
  106. content += '}\n\n'
  107. content += 'namespace dns {\n'
  108. continue
  109. if re.match('// BEGIN_RDATA_NAMESPACE', line):
  110. content += 'namespace rdata {\n'
  111. content += 'namespace ' + class_txt + ' {\n'
  112. continue
  113. if re.match('// END_ISC_NAMESPACE', line):
  114. content += '} // end of namespace "dns"\n'
  115. content += '} // end of namespace "isc"\n'
  116. continue
  117. if re.match('// END_RDATA_NAMESPACE', line):
  118. content += '} // end of namespace "' + class_txt +'"\n'
  119. content += '} // end of namespace "rdata"\n'
  120. continue
  121. if re.match('// Local Variables:', line):
  122. break
  123. content += line
  124. if re.match('// BEGIN_COMMON_DECLARATIONS', line):
  125. content += '''
  126. class AbstractMessageRenderer;\n\n'''
  127. if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
  128. content += '''
  129. explicit ''' + type_utxt + '''(const std::string& type_str);
  130. ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len);
  131. ''' + type_utxt + '''(const ''' + type_utxt + '''& other);
  132. ''' + type_utxt + '''(
  133. MasterLexer& lexer, const Name* name,
  134. MasterLoader::Options options, MasterLoaderCallbacks& callbacks);
  135. virtual std::string toText() const;
  136. virtual void toWire(isc::util::OutputBuffer& buffer) const;
  137. virtual void toWire(AbstractMessageRenderer& renderer) const;
  138. virtual int compare(const Rdata& other) const;\n\n'''
  139. rdata_header.close()
  140. return content
  141. def import_definitions(classcode2txt, typecode2txt, typeandclass):
  142. global rdata_declarations
  143. global class_definitions
  144. global classdir_mtime
  145. global rdatadef_mtime
  146. global rdatahdr_mtime
  147. if classdir_mtime < getmtime('@srcdir@/rdata'):
  148. classdir_mtime = getmtime('@srcdir@/rdata')
  149. # Sort directories before iterating through them so that the directory
  150. # list is processed in the same order on all systems. The resulting
  151. # files should compile regardless of the order in which the components
  152. # are included but... Having a fixed order for the directories should
  153. # eliminate system-dependent problems. (Note that the directory names
  154. # in BIND 10 are ASCII, so the order should be locale-independent.)
  155. dirlist = os.listdir('@srcdir@/rdata')
  156. dirlist.sort()
  157. for dir in dirlist:
  158. classdir = '@srcdir@/rdata' + os.sep + dir
  159. m = re_typecode.match(dir)
  160. if os.path.isdir(classdir) and (m != None or dir == 'generic'):
  161. if dir == 'generic':
  162. class_txt = 'generic'
  163. class_code = generic_code
  164. else:
  165. class_txt = m.group(1)
  166. class_code = m.group(2)
  167. if not class_code in classcode2txt:
  168. classcode2txt[class_code] = class_txt
  169. # Same considerations as directories regarding sorted order
  170. # also apply to files.
  171. filelist = os.listdir(classdir)
  172. filelist.sort()
  173. for file in filelist:
  174. file = classdir + os.sep + file
  175. m = re_typecode.match(os.path.split(file)[1])
  176. if m != None:
  177. type_txt = m.group(1)
  178. type_code = m.group(2)
  179. if not type_code in typecode2txt:
  180. typecode2txt[type_code] = type_txt
  181. if re.search('\.cc$', file):
  182. if rdatadef_mtime < getmtime(file):
  183. rdatadef_mtime = getmtime(file)
  184. class_definitions += import_classdef(class_txt, file)
  185. elif re.search('\.h$', file):
  186. if rdatahdr_mtime < getmtime(file):
  187. rdatahdr_mtime = getmtime(file)
  188. rdata_declarations += import_classheader(class_txt,
  189. type_txt,
  190. type_code,
  191. file)
  192. typeandclass.append((type_txt, int(type_code),
  193. (class_txt, class_txt),
  194. int(class_code)))
  195. if class_txt == 'generic':
  196. typeandclass.append((type_txt, int(type_code),
  197. (class_txt, 'in'), 1))
  198. def need_generate(file, mtime):
  199. '''Check if we need to generate the specified file.
  200. To avoid unnecessary compilation, we skip (re)generating the file when
  201. the file already exists and newer than the base file.
  202. '''
  203. if os.path.exists(file) and getmtime(file) > mtime:
  204. return False
  205. return True
  206. def generate_rdatadef(file, basemtime):
  207. if not need_generate(file, basemtime):
  208. print('skip generating ' + file);
  209. return
  210. rdata_deffile = open(file, 'w')
  211. rdata_deffile.write(heading_txt)
  212. rdata_deffile.write(class_definitions)
  213. rdata_deffile.close()
  214. def generate_rdatahdr(file, heading, declarations, basemtime):
  215. if not need_generate(file, basemtime):
  216. print('skip generating ' + file);
  217. return
  218. heading += '''
  219. #ifndef DNS_RDATACLASS_H
  220. #define DNS_RDATACLASS_H 1
  221. #include <dns/master_loader.h>
  222. namespace isc {
  223. namespace dns {
  224. class Name;
  225. class MasterLexer;
  226. class MasterLoaderCallbacks;
  227. }
  228. }
  229. '''
  230. declarations += '''
  231. #endif // DNS_RDATACLASS_H
  232. // Local Variables:
  233. // mode: c++
  234. // End:
  235. '''
  236. rdata_header = open(file, 'w')
  237. rdata_header.write(heading)
  238. rdata_header.write(declarations)
  239. rdata_header.close()
  240. def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class):
  241. placeholder = '@srcdir@/' + fileprefix + '-placeholder.h'
  242. outputfile = '@builddir@/' + fileprefix + '.h'
  243. py_outputfile = '@builddir@/python/' + fileprefix + '_constants_inc.cc'
  244. upper_key = type_or_class.upper() # TYPE or CLASS
  245. lower_key = 'rr' + type_or_class.lower() # rrtype or rrclass
  246. cap_key = type_or_class # Type or Class
  247. # We only decide whether to generate files for libdns++ files; Python
  248. # files are generated if and only if libdns++ files are generated.
  249. # In practice it should be sufficient.
  250. if (not need_generate(outputfile, basemtime) and
  251. getmtime(outputfile) > getmtime(placeholder)):
  252. print('skip generating ' + outputfile)
  253. return
  254. # Create a list of (code, code-text) pairs, where code-text is generally
  255. # upper-cased, with applying speicial filters when necessary.
  256. def convert(code_txt):
  257. # Workaround by heuristics: there's a "NULL" RR type, but it would
  258. # cause conflict with the C/C++ macro. We use Null as a special case.
  259. if code_txt == 'null':
  260. return 'Null'
  261. # Likewise, convert "nsap-ptr" to "NSAP_PTR" as a dash cannot be part
  262. # of a C/C++ variable.
  263. if code_txt == 'nsap-ptr':
  264. return 'NSAP_PTR'
  265. return code_txt.upper()
  266. codes = [ (code, convert(txt)) for code, txt in code2txt.items() ]
  267. # Dump source code for libdns++
  268. with open(placeholder, 'r') as header_temp:
  269. with open(outputfile, 'w') as header_out:
  270. header_out.write(heading_txt)
  271. for line in header_temp:
  272. header_out.write(line)
  273. if re.match('\s+// BEGIN_WELL_KNOWN_' + upper_key +
  274. '_DECLARATIONS$', line):
  275. for code in codes:
  276. header_out.write(' ' * 4 + 'static const RR' +
  277. cap_key + '& ' + code[1] + '();\n')
  278. if re.match('// BEGIN_WELL_KNOWN_' + upper_key +
  279. '_DEFINITIONS$', line):
  280. for code in codes:
  281. header_out.write('''inline const RR''' + cap_key +
  282. '''&
  283. RR''' + cap_key + '''::''' + code[1] + '''() {
  284. static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code[0] + ''');
  285. return (''' + lower_key + ''');
  286. }\n
  287. ''')
  288. # Dump source code snippet for isc.dns Python module
  289. with open(py_outputfile, 'w') as py_out:
  290. py_out.write(" // auto-generated by ../gen-rdatacode.py."
  291. " Don't edit this file.\n")
  292. py_out.write("\n")
  293. for code in codes:
  294. py_out.write('''\
  295. installClassVariable(''' + lower_key + '''_type, "''' + code[1] + '''",
  296. createRR''' + cap_key + '''Object(RR''' + \
  297. cap_key + '''::''' + code[1] + '''()));
  298. ''')
  299. def generate_rrparam(fileprefix, basemtime):
  300. placeholder = '@srcdir@/' + fileprefix + '-placeholder.cc'
  301. outputfile = '@builddir@/' + fileprefix + '.cc'
  302. if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder):
  303. print('skip generating ' + outputfile)
  304. return
  305. # sort by class, then by type
  306. typeandclassparams = ''
  307. typeandclass.sort(key = lambda x: (x[3], x[1]))
  308. for param in typeandclass:
  309. # for rrparamregistry.cc
  310. # each param is a tuple of (type_txt, type_code, class_tuple,
  311. # class_code)
  312. (type_txt, type_code, class_tuple, class_code) = param
  313. type_utxt = type_txt.upper()
  314. class_txt = class_tuple[0]
  315. class_utxt = class_tuple[1].upper()
  316. indent = ' ' * 8
  317. typeandclassparams += indent
  318. if class_tuple[1] != 'generic':
  319. typeandclassparams += 'add("' + type_utxt + '", '
  320. typeandclassparams += str(type_code) + ', "' + class_utxt
  321. typeandclassparams += '", ' + str(class_code)
  322. typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
  323. typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
  324. else:
  325. typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code)
  326. typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
  327. typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
  328. typeandclassparams += indent + '// Meta and non-implemented RR types\n'
  329. for type_code, type_txt in meta_types.items():
  330. typeandclassparams += indent + \
  331. 'addType("' + type_txt.upper() + '", ' + type_code + ');\n'
  332. typeandclassparams += indent + '// Meta classes\n'
  333. for cls_code, cls_txt in meta_classes.items():
  334. typeandclassparams += indent + \
  335. 'addClass("' + cls_txt.upper() + '", ' + cls_code + ');\n'
  336. rrparam_temp = open(placeholder, 'r')
  337. rrparam_out = open(outputfile, 'w')
  338. rrparam_out.write(heading_txt)
  339. for line in rrparam_temp.readlines():
  340. rrparam_out.write(line)
  341. if re.match('\s+// BEGIN_WELL_KNOWN_PARAMS', line):
  342. rrparam_out.write(typeandclassparams)
  343. rrparam_temp.close()
  344. rrparam_out.close()
  345. if __name__ == "__main__":
  346. try:
  347. import_definitions(classcode2txt, typecode2txt, typeandclass)
  348. generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime)
  349. generate_rdatahdr('@builddir@/rdataclass.h', heading_txt,
  350. rdata_declarations, rdatahdr_mtime)
  351. # merge auto-generated types/classes with meta maps and generate the
  352. # corresponding code.
  353. generate_typeclasscode('rrtype', rdatahdr_mtime,
  354. dict(typecode2txt, **meta_types), 'Type')
  355. generate_typeclasscode('rrclass', classdir_mtime,
  356. dict(classcode2txt, **meta_classes), 'Class')
  357. generate_rrparam('rrparamregistry', rdatahdr_mtime)
  358. except:
  359. sys.stderr.write('Code generation failed due to exception: %s\n' %
  360. sys.exc_info()[1])
  361. exit(1)