axion.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #!/bin/env python2.7
  2. # ===| Setup |=================================================================
  3. from __future__ import print_function
  4. from six import string_types
  5. from collections import OrderedDict
  6. from itertools import chain
  7. from datetime import datetime
  8. import sys
  9. import os
  10. import shutil
  11. import errno
  12. import random
  13. import argparse
  14. #%ifdef 0
  15. # Bytecode belongs in ram not on my hard drive.
  16. sys.dont_write_bytecode = True
  17. #%endif
  18. from preprocessor import Preprocessor
  19. # -----------------------------------------------------------------------------
  20. # Script Vars
  21. SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + os.sep
  22. TOPSRCDIR = SCRIPT_DIR
  23. DIST_DIR = TOPSRCDIR + 'dist/'
  24. FINAL_TARGET = DIST_DIR + 'bin/'
  25. FINAL_TARGET_FILES = []
  26. print('{0}: Starting up...'.format(__file__))
  27. # =============================================================================
  28. # ===| Functions |=============================================================
  29. def gOutput(aMsg):
  30. print(aMsg)
  31. # -----------------------------------------------------------------------------
  32. def gError(aMsg):
  33. gOutput(aMsg)
  34. exit(1)
  35. # -----------------------------------------------------------------------------
  36. def gReadFile(aFile):
  37. try:
  38. with open(os.path.abspath(aFile), 'rb') as f:
  39. rv = f.read()
  40. except:
  41. return None
  42. return rv
  43. # -----------------------------------------------------------------------------
  44. #%ifdef 0
  45. def gZoneConfig(aZones, aBasePath):
  46. # File Content
  47. zone_base = '''
  48. zone "{zone}" {lb}
  49. type {type};
  50. file "{path}/{zone}.zone";
  51. allow-query {lb} any; {rb};
  52. {rb}'''
  53. # Prep the conf content
  54. zone_content = ''
  55. # Iterate over the zones in the configuration json and fill out the conf content
  56. for _zone in aZones:
  57. # Master
  58. _content = zone_base.format(lb = "{", rb = "}",
  59. zone = _zone, type = 'master',
  60. path = aBasePath)
  61. zone_content += _content
  62. return zone_content
  63. #%endif
  64. # -----------------------------------------------------------------------------
  65. def gTargetFile(src, target = None, cmd = 'cp', defs = None, source_dir = TOPSRCDIR, final_target = FINAL_TARGET, marker='#'):
  66. global FINAL_TARGET
  67. output_prefix = ' '
  68. # Deal with final_target
  69. if final_target != FINAL_TARGET:
  70. final_target = FINAL_TARGET + final_target + os.sep
  71. if not target:
  72. target = final_target + os.path.basename(src)
  73. if src.endswith('.in'):
  74. target = target[:-3]
  75. else:
  76. target = final_target + target
  77. # Deal with source_dir
  78. if source_dir != TOPSRCDIR:
  79. source_dir = TOPSRCDIR + source_dir + os.sep
  80. if os.path.dirname(src) + os.sep + os.path.basename(src) != src:
  81. src = source_dir + src
  82. # Normalize to absolute paths
  83. src = os.path.abspath(src)
  84. target = os.path.abspath(target)
  85. # This structure defines a "rule" in the makefile/mozbuild sense but
  86. # not over-engineered and cryptic as hell.
  87. struct = {'outfile': target, 'src': src, 'cmd': cmd}
  88. if defs or struct['cmd'] == 'pp':
  89. struct['cmd'] = 'pp'
  90. struct['ppDefines'] = defs
  91. output_prefix = '* '
  92. if target.endswith('.css'):
  93. struct['ppMarker'] = '%'
  94. elif target.endswith('.py'):
  95. struct['ppMarker'] = '#%'
  96. else:
  97. struct['ppMarker'] = marker
  98. gOutput('{0}{1}'.format(output_prefix, target))
  99. return struct
  100. # -----------------------------------------------------------------------------
  101. def gProcessDirectory(aDirectory = '', aDefines = {}, aConfig = {}):
  102. global TOPSRCDIR
  103. # Vars
  104. final_target_files = []
  105. build_file = os.path.abspath(TOPSRCDIR + aDirectory + '/axion.build')
  106. gOutput('Processing {0}'.format(build_file))
  107. build_exec = gReadFile(build_file)
  108. config = {}
  109. defines = {}
  110. if not build_exec:
  111. gError('Could not read {0}'.format(build_file))
  112. # We only want UPPERCASE keys
  113. for _key, _value in aConfig.iteritems():
  114. if _key.isupper():
  115. config[_key] = _value
  116. for _key, _value in aDefines.iteritems():
  117. if _key.isupper():
  118. defines[_key] = _value
  119. # Set the environment
  120. exec_globals = {
  121. '__builtins__': {
  122. 'True': True,
  123. 'False': False,
  124. 'None': None,
  125. 'OrderedDict': OrderedDict,
  126. 'dict': dict,
  127. 'gOutput': gOutput,
  128. 'gError': gError,
  129. 'gTargetFile': gTargetFile,
  130. #%ifdef 0
  131. 'gZoneConfig': gZoneConfig,
  132. #%endif
  133. }
  134. }
  135. exec_locals = {
  136. 'SRC_DIR': aDirectory + os.sep,
  137. 'DIRS': [],
  138. 'CONFIG': config,
  139. 'DEFINES': defines,
  140. 'SRC_FILES': [],
  141. 'SRC_PP_FILES': [],
  142. 'MANUAL_TARGET_FILES': [],
  143. #%ifdef 0
  144. 'BINOC_NGX_SERVERS': [],
  145. 'BINOC_DNS_BASIC_ZONES': [],
  146. 'BINOC_DNS_ADV_ZONES': [],
  147. 'BINOC_DNS_ZONES': [],
  148. #%endif
  149. }
  150. # Exec the build script so we can harvest the locals
  151. exec(build_exec, exec_globals, exec_locals)
  152. # Add manually specified structs to final_target_files
  153. for struct in exec_locals['MANUAL_TARGET_FILES']:
  154. final_target_files += [struct]
  155. # Create targets for Preprocessed Files
  156. for file in exec_locals['SRC_PP_FILES']:
  157. final_target_files += [gTargetFile(file, cmd='pp', defs=exec_locals['DEFINES'],
  158. source_dir=aDirectory, final_target=aDirectory)]
  159. # Create targets for copy operations
  160. for file in exec_locals['SRC_FILES']:
  161. final_target_files += [gTargetFile(file, source_dir=aDirectory, final_target=aDirectory)]
  162. #%ifdef 0
  163. if 'BINOC_TARGETS' in exec_locals['CONFIG']:
  164. # Create targets for BinOC Basic amd Advanced DNS Zones
  165. for zone in exec_locals['BINOC_DNS_ZONES']:
  166. zone_defs = dict(exec_locals['DEFINES'], **{
  167. 'BIND_ZONE_DOMAIN': zone,
  168. 'BIND_ZONE_SERIAL': datetime.today().strftime('%Y%m%d%H'),
  169. })
  170. if os.path.exists(os.path.abspath(TOPSRCDIR + aDirectory + '/' + zone + '.zone.in')):
  171. final_target_files += [gTargetFile(zone + '.zone.in', cmd='pp',
  172. defs=zone_defs, source_dir=aDirectory,
  173. final_target=aDirectory)]
  174. else:
  175. final_target_files += [gTargetFile('basic.zone.in', zone + '.zone', cmd='pp',
  176. defs=zone_defs, source_dir=aDirectory,
  177. final_target=aDirectory)]
  178. # Create targets for BinOC Nginx Standard Servers
  179. for server in exec_locals['BINOC_NGX_SERVERS']:
  180. if server['NGX_SUB_DOMAIN'] == 'www':
  181. server['NGX_MAIN_SERVER'] = True
  182. final_target_files += [gTargetFile(
  183. 'standard.server.in',
  184. '{0}.{1}.server'.format(server['NGX_SUB_DOMAIN'], server['NGX_MAIN_DOMAIN']),
  185. cmd='pp', defs=server, source_dir=aDirectory, final_target=aDirectory
  186. )]
  187. #%endif
  188. # We don't do PROPER traversial but we can go to any directory we want from the
  189. # topsrcdir build file.
  190. if build_file == TOPSRCDIR + 'axion.build':
  191. if exec_locals['DIRS']:
  192. gOutput('- Found the following DIRS: {0}'.format(', '.join(exec_locals['DIRS'])))
  193. for dir in exec_locals['DIRS']:
  194. final_target_files += gProcessDirectory(dir, dict(exec_locals['DEFINES']), dict(exec_locals['CONFIG']))
  195. return final_target_files
  196. # -----------------------------------------------------------------------------
  197. def gProcessTargets(aTargets):
  198. global TOPSRCDIR
  199. global FINAL_TARGET
  200. bin_files = []
  201. for target in aTargets:
  202. # Make sure directories exist before we try opening files for read/write
  203. if not os.path.exists(os.path.dirname(target['outfile'])):
  204. try:
  205. os.makedirs(os.path.dirname(target['outfile']))
  206. except OSError as e:
  207. # Guard against race condition
  208. if e.errno != errno.EEXIST:
  209. raise
  210. # Print what are going to do
  211. gOutput('[{0}] {1} -> {2}'.format(target['cmd'],
  212. target['src'].replace(TOPSRCDIR, "." + os.sep),
  213. target['outfile']))
  214. # And do it by preprocessing or copying
  215. if target['cmd'] == 'pp':
  216. defines = {}
  217. # If any defines are None then the are "undef'd"
  218. for _key, _value in target['ppDefines'].iteritems():
  219. if _value is None:
  220. continue
  221. defines[_key] = _value
  222. # Create the preprocessor
  223. pp = Preprocessor(defines=defines, marker=target['ppMarker'])
  224. # We always do substs
  225. pp.do_filter("substitution")
  226. # Preprocess.
  227. with open(target['outfile'], "w") as output:
  228. with open(target['src'], "r") as input:
  229. pp.processFile(input=input, output=output)
  230. elif target['cmd'] == 'cp':
  231. # We be just a-copyin so do it.
  232. shutil.copy(target['src'], target['outfile'])
  233. else:
  234. gError('Cannot process {0} because the required command could be executed.'.format(target['outfile']))
  235. # Make sure the
  236. if not os.path.exists(target['outfile']):
  237. gError('An expected output file ({0}) does not actually exist.'.format(target['outfile']))
  238. bin_files += [ target['outfile'] ]
  239. return bin_files
  240. # =============================================================================
  241. # ===| Main |==================================================================
  242. # Attempt to remove the dist dir
  243. if os.path.exists(DIST_DIR):
  244. try:
  245. shutil.rmtree(DIST_DIR)
  246. except:
  247. try:
  248. shutil.rmtree(DIST_DIR)
  249. except:
  250. gError('Could not remove the dist dir. Please try again.')
  251. kNewLine = '\n'
  252. kDot = "."
  253. kDash = '-'
  254. kSpace = " "
  255. kFullLine = '==============================================================================='
  256. gOutput(kFullLine + kNewLine + kSpace + kSpace +
  257. 'Target acquisition (-=info, *=preprocess, blank=copy)' +
  258. kNewLine + kFullLine)
  259. # Process TOPSRCDIR
  260. FINAL_TARGET_FILES += gProcessDirectory()
  261. gOutput('')
  262. # -----------------------------------------------------------------------------
  263. output_lines = [
  264. "Watch and learn, everybody. Watch and learn.",
  265. "Alright, check THIS out.",
  266. "Alright, stand back, everybody.",
  267. "It's my big chance!",
  268. "Here it comes, pal!",
  269. "Locked and loaded!",
  270. ]
  271. gOutput(kFullLine + kNewLine + kSpace + kSpace +
  272. random.choice(output_lines) + kNewLine + kFullLine)
  273. # Process all targets
  274. gProcessTargets(FINAL_TARGET_FILES)
  275. # =============================================================================