waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

run_do_script.py (4989B)


      1 #!/usr/bin/env python
      2 # encoding: utf-8
      3 # Hans-Martin von Gaudecker, 2012
      4 
      5 """
      6 Run a Stata do-script in the directory specified by **ctx.bldnode**. The
      7 first and only argument will be the name of the do-script (no extension),
      8 which can be accessed inside the do-script by the local macro `1'. Useful
      9 for keeping a log file.
     10 
     11 The tool uses the log file that is automatically kept by Stata only 
     12 for error-catching purposes, it will be destroyed if the task finished
     13 without error. In case of an error in **some_script.do**, you can inspect
     14 it as **some_script.log** in the **ctx.bldnode** directory.
     15 
     16 Note that Stata will not return an error code if it exits abnormally -- 
     17 catching errors relies on parsing the log file mentioned before. Should
     18 the parser behave incorrectly please send an email to hmgaudecker [at] gmail.
     19 
     20 **WARNING**
     21 
     22     The tool will not work if multiple do-scripts of the same name---but in
     23     different directories---are run at the same time! Avoid this situation.
     24 
     25 Usage::
     26 
     27     ctx(features='run_do_script', 
     28         source='some_script.do',
     29         target=['some_table.tex', 'some_figure.eps'],
     30         deps='some_data.csv')
     31 """
     32 
     33 
     34 import os, re, sys
     35 from waflib import Task, TaskGen, Logs
     36 
     37 if sys.platform == 'darwin':
     38 	STATA_COMMANDS = ['Stata64MP', 'StataMP',
     39 								'Stata64SE', 'StataSE', 
     40 								'Stata64', 'Stata']
     41 	STATAFLAGS = '-e -q do'
     42 	STATAENCODING = 'MacRoman'
     43 elif sys.platform.startswith('linux'):
     44 	STATA_COMMANDS = ['stata-mp', 'stata-se', 'stata']
     45 	STATAFLAGS = '-b -q do'
     46 	# Not sure whether this is correct...
     47 	STATAENCODING = 'Latin-1'
     48 elif sys.platform.lower().startswith('win'):
     49 	STATA_COMMANDS = ['StataMP-64', 'StataMP-ia',
     50 								'StataMP', 'StataSE-64',
     51 								'StataSE-ia', 'StataSE',
     52 								'Stata-64', 'Stata-ia',
     53 								'Stata.e', 'WMPSTATA',
     54 								'WSESTATA', 'WSTATA']
     55 	STATAFLAGS = '/e do'
     56 	STATAENCODING = 'Latin-1'
     57 else:
     58 	raise Exception("Unknown sys.platform: %s " % sys.platform)
     59 
     60 def configure(ctx):
     61 	ctx.find_program(STATA_COMMANDS, var='STATACMD', errmsg="""\n
     62 No Stata executable found!\n\n
     63 If Stata is needed:\n
     64 	1) Check the settings of your system path.
     65 	2) Note we are looking for Stata executables called: %s
     66 	   If yours has a different name, please report to hmgaudecker [at] gmail\n
     67 Else:\n
     68 	Do not load the 'run_do_script' tool in the main wscript.\n\n""" % STATA_COMMANDS)
     69 	ctx.env.STATAFLAGS = STATAFLAGS
     70 	ctx.env.STATAENCODING = STATAENCODING
     71 
     72 class run_do_script_base(Task.Task):
     73 	"""Run a Stata do-script from the bldnode directory."""
     74 	run_str = '"${STATACMD}" ${STATAFLAGS} "${SRC[0].abspath()}" "${DOFILETRUNK}"'
     75 	shell = True
     76 
     77 class run_do_script(run_do_script_base):
     78 	"""Use the log file automatically kept by Stata for error-catching.
     79 	Erase it if the task finished without error. If not, it will show 
     80 	up as do_script.log in the bldnode directory.
     81 	"""
     82 	def run(self):
     83 		run_do_script_base.run(self)
     84 		ret, log_tail  = self.check_erase_log_file()
     85 		if ret:
     86 			Logs.error("""Running Stata on %r failed with code %r.\n\nCheck the log file %s, last 10 lines\n\n%s\n\n\n""",
     87 				self.inputs[0], ret, self.env.LOGFILEPATH, log_tail)
     88 		return ret
     89 
     90 	def check_erase_log_file(self):
     91 		"""Parse Stata's default log file and erase it if everything okay.
     92 
     93 		Parser is based on Brendan Halpin's shell script found here:
     94 			http://teaching.sociology.ul.ie/bhalpin/wordpress/?p=122
     95 		"""
     96 
     97 		if sys.version_info.major >= 3:
     98 			kwargs = {'file': self.env.LOGFILEPATH, 'mode': 'r', 'encoding': self.env.STATAENCODING}
     99 		else:
    100 			kwargs = {'name': self.env.LOGFILEPATH, 'mode': 'r'}
    101 		with open(**kwargs) as log:
    102 			log_tail = log.readlines()[-10:]
    103 			for line in log_tail:
    104 				error_found = re.match(r"r\(([0-9]+)\)", line)
    105 				if error_found:
    106 					return error_found.group(1), ''.join(log_tail)
    107 				else:
    108 					pass
    109 		# Only end up here if the parser did not identify an error.
    110 		os.remove(self.env.LOGFILEPATH)
    111 		return None, None
    112 
    113 
    114 @TaskGen.feature('run_do_script')
    115 @TaskGen.before_method('process_source')
    116 def apply_run_do_script(tg):
    117 	"""Task generator customising the options etc. to call Stata in batch
    118 	mode for running a do-script.
    119 	"""
    120 
    121 	# Convert sources and targets to nodes
    122 	src_node = tg.path.find_resource(tg.source)
    123 	tgt_nodes = [tg.path.find_or_declare(t) for t in tg.to_list(tg.target)]
    124 
    125 	tsk = tg.create_task('run_do_script', src=src_node, tgt=tgt_nodes)
    126 	tsk.env.DOFILETRUNK = os.path.splitext(src_node.name)[0]
    127 	tsk.env.LOGFILEPATH = os.path.join(tg.bld.bldnode.abspath(), '%s.log' % (tsk.env.DOFILETRUNK))
    128 
    129 	# dependencies (if the attribute 'deps' changes, trigger a recompilation)
    130 	for x in tg.to_list(getattr(tg, 'deps', [])):
    131 		node = tg.path.find_resource(x)
    132 		if not node:
    133 			tg.bld.fatal('Could not find dependency %r for running %r' % (x, src_node.abspath()))
    134 		tsk.dep_nodes.append(node)
    135 	Logs.debug('deps: found dependencies %r for running %r', tsk.dep_nodes, src_node.abspath())
    136 
    137 	# Bypass the execution of process_source by setting the source to an empty list
    138 	tg.source = []
    139