def do_action(params): f = Forcheck(params["files"], fortran_standard=params["standard"], emulate_compiler=params["emulation"], free_format=params["free_format"], extra_opts=params["extra_opts"]) if params["pretend"]: print " ".join(f.get_command()) sys.exit(0) # run forcheck f.run() forcheck_output = f.get_tmp_filename() # file deleted by f.__del__() # parse parser = ForcheckParser(forcheck_output, ignore_list=params["ignore_list"]) # result state state = parser.state state.run_data = f.get_run_data() # generate output writer = ResultWriter(parser.state, params["outdir"]) writer.run() p_info("\nAll done. View '%s/index.html' for results." % params["outdir"])
def run(self): p_info("\nWriting HTML output to '%s'" % self.outdir) if self.outdir and not os.path.isdir(self.outdir): os.makedirs(self.outdir) self._gen_assets() self._gen_event_pages() self._gen_source_pages() self._gen_index()
def _gen_index(self): outfile = os.path.join(self.outdir, "index.html") p_info(" - Generating %s" % outfile) ctx = self.default_context.copy() ctx["event_summary"] = self.events ctx["FCKDIR"] = os.environ["FCKDIR"] ctx["FCKCNF"] = os.environ["FCKCNF"] ctx["FCKPWD"] = os.environ["FCKPWD"] if hasattr(self.state, "run_data"): ctx.update(self.state.run_data) with open(outfile, 'w') as f: f.write(render("index.html", ctx))
def _gen_source_pages(self): p_info(" - Generating marked-up source files") ctx = self.default_context.copy() for filename, event_instances in self.state.file_events.iteritems(): ctx["code_block"], subpath, depth = self._format_source(filename) outfile = os.path.join(self.outdir, subpath) p_verbose(" -- %s" % outfile) ctx["to_root"] = "../" * depth ctx["filename"] = filename outdir = os.path.dirname(outfile) if outdir and not os.path.isdir(outdir): os.makedirs(outdir) with open(outfile, 'w') as f: f.write(render("code_source.html", ctx).encode('utf-8'))
def _gen_event_pages(self): depth = 1 eventdir = os.path.join(self.outdir, "event") p_info(" - Generating event summaries") if not os.path.isdir(eventdir): os.makedirs(eventdir) ctx = self.default_context.copy() ctx["to_root"] = "../" * depth for e in self.events: outfile = "%s/%s.html" % (eventdir, e.code.replace(' ', '_')) ctx["event"] = e ctx["event_instances"] = self.state.event_instances[e.code] with open(outfile, 'w') as f: f.write(render("event.html", ctx))
def __init__(self, input_files, fortran_standard="95", emulate_compiler="gfortran", free_format=False, extra_opts=None): self.rc = None self.input_files = input_files self.extra_opts = extra_opts # create tempfile file for use as staging area for forcheck .lst file tmp_fd, self.tmpfile = mkstemp(suffix=".lst") os.close(tmp_fd) # The following are set by call to self._locate_forcheck() self.supported_emulators = [] self.cnfdir = None self.forcheck_exe = None self.forcheck_version = None self._locate_forcheck() # we support only version 14.2 and above ver = self.get_version() # store layout format and language standard self.free_format = free_format if fortran_standard not in SUPPORTED_STANDARDS: raise CheckfortException("Unsupported Fortran standard. " "Possible options: " + ", ".join(SUPPORTED_STANDARDS.keys())) self.fortran_standard = fortran_standard # select compiler emulation if emulate_compiler not in self.supported_emulators: raise CheckfortException("Unsuppoted compiler emulation. " "Possible options: " + ", ".join(self.supported_emulators)) self.emulate_compiler = emulate_compiler os.environ["FCKCNF"] = os.path.join(self.cnfdir, "%s.cnf" % emulate_compiler) p_info(" - compiler emilation: %s" % emulate_compiler)
def run(self): out = "forcheck.log" p_info("\nRunning forcheck (stdout written to %s)" % out) with open(out, "w") as fout: # use pexpect.spawn instead of subprocess.Popen so we can get # real-time output from forcheck (Popen is subject to stdout being # buffered when redirected to PIPE). cmd = self.get_command() child = pexpect.spawn(cmd[0], args=cmd[1:], logfile=fout) self._store_prev = ("", "") while True: try: child.expect('\n') self._report_runtime_message(child.before) except pexpect.EOF: break child.close() self.rc = child.exitstatus try: p_info("\nDONE. (rc=%d, %s)" % (self.rc, EXIT_CODES[self.rc])) except KeyError: p_error("FAILED (rc=%d). See %s for details" % (self.rc, out))
def _report_runtime_message(self, line): """Selectively print content from forcheck runtime output""" line = line.strip() if line.startswith("-- "): if line.startswith("-- file: "): p_verbose(" - %s" % line.split(None, 2)[2]) elif not line.startswith("-- commandline") and \ not line.startswith("-- messages presented"): p_verbose(line) if not verbose_enabled(): p_info(".", "") elif line.startswith("FCK-- "): err = line.split(None, 1)[1] culprit, filename = self._store_prev if not filename.startswith("("): filename = "" # not specified p_warn("%s - %s %s\n" % (err, culprit, filename)) elif not verbose_enabled() and line.startswith("- file"): p_info(".", "") self._store_prev = (line, self._store_prev[0])
def _parse(self): p_info("\nParsing forcheck listfile") if self.state.ignore_list: p_info("(ignoring the following forcheck events: " "%s)" % ", ".join(str(x) for x in self.state.ignore_list)) stages = iter(( {"name": "file events", "end_marker": "global program analysis:", "parser": FileEvent(self.state)}, {"name": "global events", "end_marker": "program_units and procedures analysed:", "parser": GlobalEvent(self.state)}, {"name": "program units", # "end_marker": "*END OF ANALYSIS*" # no longer appear in >14.3 "end_marker": "messages presented:", "parser": None}, {"name": "forcheck summary", "end_marker": None, "parser": SummaryEvent(self.state)}, )) lines = ("", "", "") # (current, previous, previous-1) stage = stages.next() p_info(" - Parsing %s" % stage["name"]) def forward_to_content(file_iterator): """ Forwards file iterator the the actual content, then returns the source file addressed by forcheck output page """ # each new page starts with a header line = next(file_iterator) assert line.startswith("FORCHECK"), "Unexpected listfile format" # this is followed by "(options...) target_file" if the output is # file specific line = next(file_iterator) if line.strip(): target_file = line.rsplit(None, 1)[-1] line = next(file_iterator) # following line should be blank assert not line.strip(), "Unexpected listfile format" else: target_file = None return target_file with open(self.listfile) as f: target_file = forward_to_content(f) for L in f: if L.startswith("\f"): # new page. forward to content target_file = forward_to_content(f) continue lines = (L.strip(), lines[0], lines[1]) # shift if lines[0] == stage["end_marker"]: stage = stages.next() p_info(" - Parsing %s" % stage["name"]) elif stage["parser"]: # if event has a parser stage["parser"].slurp(target_file, *lines) if self.state.debug_required: import shutil listfile_out = "forcheck_listfile.debug" shutil.copy(self.listfile, listfile_out) p_debug( "There appears to be a problem in the parsing. \n" " Forcheck results written to %s. \n" " Please send the file and the checkfort version info\n" " to the checkfort devs for further investigation" % (listfile_out, )) else: p_verbose("Parse successful")
def _locate_forcheck(self): """ Detect required environment variables (FCKDIR, FCKCNF). From those values, locate forchk binary and detects list of supported compiler emulators. sets self.cnfdir, self.forcheck_exe and self.supported_emulators """ p_info("\nLocating forcheck") if "FCKPWD" not in os.environ: raise CheckfortException("FCKPWD environment var not set") try: fdir = os.environ["FCKDIR"] except KeyError: raise CheckfortException("FCKDIR environment var not set") # locate exe candidates = map(lambda x: os.path.join(fdir, x, "forchk"), ("bin", ".")) try: found = (x for x in candidates if os.path.isfile(x)).next() except StopIteration: raise CheckfortException("Could not find 'forchk' binary") self.forcheck_exe = os.path.realpath(os.path.join(fdir, found)) # locate g95.cnf and assume all cnf files are in the same dir candidates = map(lambda x: os.path.join(fdir, x, "g95.cnf"), ("share/forcheck", ".")) try: found = (x for x in candidates if os.path.isfile(x)).next() except StopIteration: raise CheckfortException("Could not find '*.cnf' files") self.cnfdir = os.path.dirname(os.path.join(fdir, found)) # detect list of supported emulators self.supported_emulators = [x[:-4] for x in sieve(self.cnfdir, "*.cnf")] # guess version number by doing a trial run of forchk try: child = subprocess.Popen([self.forcheck_exe, "-batch"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) except: raise CheckfortException("Could not run " + self.forcheck_exe) # extract version string from output header first_line = child.communicate()[0].split("\n", 1)[0] last_col = first_line.rsplit(None, 1)[1] try: ver = re.match(r"V(\d+)\.(\d+)\.(\d+)", last_col).groups() self.forcheck_version = map(int, ver) except AttributeError: raise CheckfortException( self.forcheck_exe + " not producing expected output") p_info(" - install dir: %s" % fdir) p_info(" - executable: %s" % self.forcheck_exe) p_info(" - version: %s" % ".".join(str(v) for v in self.get_version())) # compare min version min_ver = tuple(int(x) for x in MIN_VESION.split(".")) if tuple(self.forcheck_version[:2]) < min_ver[:2]: p_error("Unsupported Forcheck version " "(version >=%s expected)." % MIN_VESION)
def _gen_assets(self): outfile = os.path.join(self.outdir, "style.css") p_info(" - Generating %s" % outfile) with open(outfile, 'w') as f: f.write(render("style.css")) f.write(HtmlFormatter(**self.fmt_args).get_style_defs())
def main(): o, a = parse_options() cleaned = {} # store validated input options cleaned["outdir"] = o.outdir cleaned["pretend"] = bool(o.pretend) cleaned["extra_opts"] = shlex.split(o.extra_opts or "") # --compiler-emulation can only be checked once Forcheck is found. # Accept anything for now cleaned["emulation"] = o.emulation # Set log level if o.quiet or cleaned["pretend"]: set_silent_mode() elif o.verbose: set_verbose_mode() if o.debug: set_debug_mode() # Once output verbosity set, we can print the output header p_info("%s" % header) # check allowed standards if o.standard not in supported_standards: p_error("Unsupported fortran standard (%s). Options: %s" % (o.standard, ", ".join(supported_standards))) else: cleaned["standard"] = o.standard # deal with optional --free-form if o.free_form: if cleaned["standard"] == '77': p_warn("Fortran 77 does not support free format. Ignoring " "--free-form (-f) option.") o.free_form = False cleaned["free_format"] = bool(o.free_form) # check --ignore-err-codes (must be comma separated numeric codes) try: cleaned["ignore_list"] = list(set(int(x) for x in o.ignore.split(",") if x.strip())) except ValueError: p_error("Invalid value for --ignore-err-codes (-i). " "Expecting comma-separated list of numeric values") # read target files form positional args and --input-file option targets = a[:] # get targets from arguments if o.input_file: # get targets from file specified with --input-file try: targets.extend(InputFileReader(o.input_file).get_entries()) except IOError: p_error("Input file not readable: %s" % o.input_file) if not targets: p_error("No inputs provided.") # file extensions to search for if o.extensions: ext_list = [x.strip() for x in o.extensions.split(",")] else: ext_list = default_extensions # search and validate target files if any(os.path.isdir(x) for x in targets): p_info("Searching directories for files with the following " "extensions : %s" % " ".join("*.%s" % x for x in ext_list)) try: filelist = FileList(targets, ext_list) except CheckfortException, e: p_error(e)