gen-rdatacode.py.in 14 KB

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