gen-rdatacode.py.in 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #!@PYTHON@
  2. # Copyright (C) 2010 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. # new_rdata_factory_users[] is a list of tuples of the form (rrtype,
  24. # rrclass). Items in the list use the (new) RdataFactory class, and
  25. # items which are not in the list use OldRdataFactory class.
  26. # Note: rrtype and rrclass must be specified in lowercase in
  27. # new_rdata_factory_users.
  28. #
  29. # Example:
  30. # new_rdata_factory_users = [('a', 'in'), ('a', 'ch')]
  31. new_rdata_factory_users = []
  32. re_typecode = re.compile('([\da-z]+)_(\d+)')
  33. classcode2txt = {}
  34. typecode2txt = {}
  35. typeandclass = []
  36. generic_code = 65536 # something larger than any code value
  37. rdata_declarations = ''
  38. class_definitions = ''
  39. classdir_mtime = 0
  40. rdatadef_mtime = 0
  41. rdatahdr_mtime = 0
  42. heading_txt = '''///////////////
  43. ///////////////
  44. /////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py.
  45. /////////////// DO NOT EDIT!
  46. ///////////////
  47. ///////////////
  48. '''
  49. def import_classdef(class_txt, file):
  50. content = ''
  51. rdata_source = open(file, 'r')
  52. for line in rdata_source.readlines():
  53. if re.match('// BEGIN_ISC_NAMESPACE', line):
  54. content += 'namespace isc {\n'
  55. content += 'namespace dns {\n'
  56. continue
  57. if re.match('// BEGIN_RDATA_NAMESPACE', line):
  58. content += 'namespace rdata {\n'
  59. content += 'namespace ' + class_txt + ' {\n'
  60. continue
  61. if re.match('// END_ISC_NAMESPACE', line):
  62. content += '} // end of namespace "dns"\n'
  63. content += '} // end of namespace "isc"\n'
  64. continue
  65. if re.match('// END_RDATA_NAMESPACE', line):
  66. content += '} // end of namespace "' + class_txt +'"\n'
  67. content += '} // end of namespace "rdata"\n'
  68. continue
  69. content += line
  70. rdata_source.close()
  71. return content
  72. def import_classheader(class_txt, type_txt, type_code, file):
  73. type_utxt = type_txt.upper()
  74. class_utxt = class_txt.upper()
  75. # for each CLASS_n/TYPE_m.h
  76. rdata_header = open(file, 'r')
  77. content = ''
  78. guard_macro = class_txt.upper() + '_' + type_txt.upper()
  79. guard_macro += '_' + type_code + '_H'
  80. for line in rdata_header.readlines():
  81. if re.match('// BEGIN_HEADER_GUARD', line):
  82. content += '#ifndef ' + guard_macro + '\n'
  83. content += '#define ' + guard_macro + ' 1\n'
  84. continue
  85. if re.match('// END_HEADER_GUARD', line):
  86. content += '#endif // ' + guard_macro + '\n'
  87. continue
  88. if re.match('// BEGIN_ISC_NAMESPACE', line):
  89. content += 'namespace isc {\n'
  90. content += 'namespace util {\n'
  91. content += '''
  92. class InputBuffer;
  93. class OutputBuffer;\n'''
  94. content += '}\n\n'
  95. content += 'namespace dns {\n'
  96. continue
  97. if re.match('// BEGIN_RDATA_NAMESPACE', line):
  98. content += 'namespace rdata {\n'
  99. content += 'namespace ' + class_txt + ' {\n'
  100. continue
  101. if re.match('// END_ISC_NAMESPACE', line):
  102. content += '} // end of namespace "dns"\n'
  103. content += '} // end of namespace "isc"\n'
  104. continue
  105. if re.match('// END_RDATA_NAMESPACE', line):
  106. content += '} // end of namespace "' + class_txt +'"\n'
  107. content += '} // end of namespace "rdata"\n'
  108. continue
  109. if re.match('// Local Variables:', line):
  110. break
  111. content += line
  112. if re.match('// BEGIN_COMMON_DECLARATIONS', line):
  113. content += '''
  114. class AbstractMessageRenderer;\n\n'''
  115. if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
  116. content += '''
  117. explicit ''' + type_utxt + '''(const std::string& type_str);
  118. ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len);
  119. ''' + type_utxt + '''(const ''' + type_utxt + '''& other);
  120. ''' + type_utxt + '''(
  121. MasterLexer& lexer, const Name* name,
  122. MasterLoader::Options options, MasterLoaderCallbacks& callbacks);
  123. virtual std::string toText() const;
  124. virtual void toWire(isc::util::OutputBuffer& buffer) const;
  125. virtual void toWire(AbstractMessageRenderer& renderer) const;
  126. virtual int compare(const Rdata& other) const;\n\n'''
  127. rdata_header.close()
  128. return content
  129. def import_definitions(classcode2txt, typecode2txt, typeandclass):
  130. global rdata_declarations
  131. global class_definitions
  132. global classdir_mtime
  133. global rdatadef_mtime
  134. global rdatahdr_mtime
  135. if classdir_mtime < getmtime('@srcdir@/rdata'):
  136. classdir_mtime = getmtime('@srcdir@/rdata')
  137. # Sort directories before iterating through them so that the directory
  138. # list is processed in the same order on all systems. The resulting
  139. # files should compile regardless of the order in which the components
  140. # are included but... Having a fixed order for the directories should
  141. # eliminate system-dependent problems. (Note that the drectory names
  142. # in BIND 10 are ASCII, so the order should be locale-independent.)
  143. dirlist = os.listdir('@srcdir@/rdata')
  144. dirlist.sort()
  145. for dir in dirlist:
  146. classdir = '@srcdir@/rdata' + os.sep + dir
  147. m = re_typecode.match(dir)
  148. if os.path.isdir(classdir) and (m != None or dir == 'generic'):
  149. if dir == 'generic':
  150. class_txt = 'generic'
  151. class_code = generic_code
  152. else:
  153. class_txt = m.group(1)
  154. class_code = m.group(2)
  155. if not class_code in classcode2txt:
  156. classcode2txt[class_code] = class_txt
  157. # Same considerations as directories regarding sorted order
  158. # also apply to files.
  159. filelist = os.listdir(classdir)
  160. filelist.sort()
  161. for file in filelist:
  162. file = classdir + os.sep + file
  163. m = re_typecode.match(os.path.split(file)[1])
  164. if m != None:
  165. type_txt = m.group(1)
  166. type_code = m.group(2)
  167. if not type_code in typecode2txt:
  168. typecode2txt[type_code] = type_txt
  169. if re.search('\cc$', file):
  170. if rdatadef_mtime < getmtime(file):
  171. rdatadef_mtime = getmtime(file)
  172. class_definitions += import_classdef(class_txt, file)
  173. elif re.search('\h$', file):
  174. if rdatahdr_mtime < getmtime(file):
  175. rdatahdr_mtime = getmtime(file)
  176. rdata_declarations += import_classheader(class_txt,
  177. type_txt,
  178. type_code,
  179. file)
  180. typeandclass.append((type_txt, int(type_code),
  181. (class_txt, class_txt),
  182. int(class_code)))
  183. if class_txt == 'generic':
  184. typeandclass.append((type_txt, int(type_code),
  185. (class_txt, 'in'), 1))
  186. def need_generate(file, mtime):
  187. '''Check if we need to generate the specified file.
  188. To avoid unnecessary compilation, we skip (re)generating the file when
  189. the file already exists and newer than the base file.
  190. '''
  191. if os.path.exists(file) and getmtime(file) > mtime:
  192. return False
  193. return True
  194. def generate_rdatadef(file, basemtime):
  195. if not need_generate(file, basemtime):
  196. print('skip generating ' + file);
  197. return
  198. rdata_deffile = open(file, 'w')
  199. rdata_deffile.write(heading_txt)
  200. rdata_deffile.write(class_definitions)
  201. rdata_deffile.close()
  202. def generate_rdatahdr(file, heading, declarations, basemtime):
  203. if not need_generate(file, basemtime):
  204. print('skip generating ' + file);
  205. return
  206. heading += '''
  207. #ifndef DNS_RDATACLASS_H
  208. #define DNS_RDATACLASS_H 1
  209. #include <dns/master_loader.h>
  210. namespace isc {
  211. namespace dns {
  212. class Name;
  213. class MasterLexer;
  214. class MasterLoaderCallbacks;
  215. }
  216. }
  217. '''
  218. declarations += '''
  219. #endif // DNS_RDATACLASS_H
  220. // Local Variables:
  221. // mode: c++
  222. // End:
  223. '''
  224. rdata_header = open(file, 'w')
  225. rdata_header.write(heading)
  226. rdata_header.write(declarations)
  227. rdata_header.close()
  228. def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class):
  229. placeholder = '@srcdir@/' + fileprefix + '-placeholder.h'
  230. outputfile = '@builddir@/' + fileprefix + '.h'
  231. upper_key = type_or_class.upper() # TYPE or CLASS
  232. lower_key = 'rr' + type_or_class.lower() # rrtype or rrclass
  233. cap_key = type_or_class # Type or Class
  234. if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder):
  235. print('skip generating ' + outputfile)
  236. return
  237. declarationtxt = ''
  238. deftxt = ''
  239. for code in code2txt.keys():
  240. codetxt = code2txt[code].upper()
  241. declarationtxt += ' ' * 4 + 'static const RR' + cap_key + '& ' + codetxt + '();\n'
  242. deftxt += '''inline const RR''' + cap_key + '''&
  243. RR''' + cap_key + '''::''' + codetxt + '''() {
  244. static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code + ''');
  245. return (''' + lower_key + ''');
  246. }\n
  247. '''
  248. header_temp = open(placeholder, 'r')
  249. header_out = open(outputfile, 'w')
  250. header_out.write(heading_txt)
  251. for line in header_temp.readlines():
  252. header_out.write(line)
  253. if re.match('\s+// BEGIN_WELL_KNOWN_' + upper_key + '_DECLARATIONS$', line):
  254. header_out.write(declarationtxt)
  255. if re.match('// BEGIN_WELL_KNOWN_' + upper_key + '_DEFINITIONS$', line):
  256. header_out.write('\n' + deftxt)
  257. header_out.close()
  258. header_temp.close()
  259. def generate_rrparam(fileprefix, basemtime):
  260. placeholder = '@srcdir@/' + fileprefix + '-placeholder.cc'
  261. outputfile = '@builddir@/' + fileprefix + '.cc'
  262. if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder):
  263. print('skip generating ' + outputfile)
  264. return
  265. # sort by class, then by type
  266. typeandclassparams = ''
  267. typeandclass.sort(key = lambda x: (x[3], x[1]))
  268. for param in typeandclass:
  269. # for rrparamregistry.cc
  270. # each param is a tuple of (type_txt, type_code, class_tuple,
  271. # class_code)
  272. (type_txt, type_code, class_tuple, class_code) = param
  273. type_utxt = type_txt.upper()
  274. class_txt = class_tuple[0]
  275. class_utxt = class_tuple[1].upper()
  276. indent = ' ' * 8
  277. typeandclassparams += indent
  278. # By default, we use OldRdataFactory (see bug #2497). If you
  279. # want to pick RdataFactory for a particular type, add it to
  280. # new_rdata_factory_users.
  281. if ((type_txt.lower(), class_txt.lower()) in new_rdata_factory_users) or \
  282. ((class_txt.lower() == 'in') and \
  283. ((type_txt.lower(), 'generic') in new_rdata_factory_users)):
  284. rdf_class = 'RdataFactory'
  285. else:
  286. rdf_class = 'OldRdataFactory'
  287. if class_tuple[1] != 'generic':
  288. typeandclassparams += 'add("' + type_utxt + '", '
  289. typeandclassparams += str(type_code) + ', "' + class_utxt
  290. typeandclassparams += '", ' + str(class_code)
  291. typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
  292. typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
  293. else:
  294. typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code)
  295. typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
  296. typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
  297. rrparam_temp = open(placeholder, 'r')
  298. rrparam_out = open(outputfile, 'w')
  299. rrparam_out.write(heading_txt)
  300. for line in rrparam_temp.readlines():
  301. rrparam_out.write(line)
  302. if re.match('\s+// BEGIN_WELL_KNOWN_PARAMS', line):
  303. rrparam_out.write(typeandclassparams)
  304. rrparam_temp.close()
  305. rrparam_out.close()
  306. if __name__ == "__main__":
  307. try:
  308. import_definitions(classcode2txt, typecode2txt, typeandclass)
  309. generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime)
  310. generate_rdatahdr('@builddir@/rdataclass.h', heading_txt,
  311. rdata_declarations, rdatahdr_mtime)
  312. generate_typeclasscode('rrtype', rdatahdr_mtime, typecode2txt, 'Type')
  313. generate_typeclasscode('rrclass', classdir_mtime,
  314. classcode2txt, 'Class')
  315. generate_rrparam('rrparamregistry', rdatahdr_mtime)
  316. except:
  317. sys.stderr.write('Code generation failed due to exception: %s\n' %
  318. sys.exc_info()[1])
  319. exit(1)