def __init__(self, cwd=None): """ Initialize the environment. The optional argument is the path to the reference directory for compilation, by default it is the current working directory. """ self.kpse_msg = { "mktextfm": _("making font metrics for \\g<arg>"), "mktexmf": _("making font \\g<arg>"), "mktexpk": _("making bitmap for font \\g<arg>") } if cwd is None: cwd = os.getcwd() self.vars = Variables(items={'cwd': cwd, '_environment': self}) self.path = [cwd] self.conv_prefs = {} # Represents a set of dependency nodes. Nodes can be accessed by absolute # path name using the dictionary interface. self.depends = dict() self.converter = Converter(self.depends) self.converter.read_ini(os.path.join(moddir, 'rules.ini')) self.is_in_unsafe_mode_ = False self.doc_requires_shell_ = False self.main = None self.final = None
def run (self): msg.progress(_("running: %s") % ' '.join(self.command)) process = Popen(self.command, stdin=devnull(), stdout=self.stdout) if process.wait() != 0: msg.error(_("execution of %s failed") % self.command[0]) return False return True
def do_read(self, args): if len(args) != 1: raise rubber.SyntaxError( _("invalid syntax for directive '%s'") % "read") name = args[0] saved_vars = self.vars try: self.vars = self.vars.copy() self.vars["file"] = name self.vars["line"] = None with open(name, encoding='utf_8', errors='replace') as file: lineno = 0 for line in file: lineno += 1 line = line.strip() if line == "" or line[0] == "%": continue self.vars["line"] = lineno lst = parse_line(line, self.vars) self.command(lst[0], lst[1:]) except IOError: msg.warning( rubber.util._format(self.vars, _("cannot read option file %s") % name)) finally: self.vars = saved_vars
def process_source_pipe(env, pipe_tempfile, options): """ Build the document, and dump the result on stdout. """ try: build(options, RUBBER_PIPE, env) filename = env.final.primary_product() try: # dump the results on standard output with open(filename, "rb") as output: shutil.copyfileobj(output, sys.stdout.buffer) except IOError: raise rubber.GenericError( _("error copying the product '%s' to stdout") % filename) finally: # clean the intermediate files if not options.keep: for node in env.final.all_producers(): node.clean() cache_path = env.main.basename('.rubbercache') if os.path.exists(cache_path): msg.debug(_("removing %s"), cache_path) os.remove(cache_path) if os.path.exists(pipe_tempfile): msg.info(_("removing %s"), pipe_tempfile) os.remove(pipe_tempfile)
def __init__ (self, cwd=None): """ Initialize the environment. The optional argument is the path to the reference directory for compilation, by default it is the current working directory. """ self.kpse_msg = { "mktextfm" : _("making font metrics for \\g<arg>"), "mktexmf" : _("making font \\g<arg>"), "mktexpk" : _("making bitmap for font \\g<arg>") } if cwd is None: cwd = os.getcwd() self.vars = Variables(items = { 'cwd': cwd, '_environment': self }) self.path = [cwd] self.conv_prefs = {} # Represents a set of dependency nodes. Nodes can be accessed by absolute # path name using the dictionary interface. self.depends = dict() self.converter = Converter(self.depends) self.converter.read_ini(os.path.join(moddir, 'rules.ini')) self.is_in_unsafe_mode_ = False self.doc_requires_shell_ = False self.main = None self.final = None
def should_make(self): """ Check the dependencies. Return true if this node has to be recompiled, i.e. if some dependency is modified. Nothing recursive is done here. """ if not self.date: return True for source_name in self.sources: source = self.set[source_name] # FIXME complain if source has been modified in an unplanned way # NB: we ignore the case source.date == None (missing dependency) here. # NB2: to be extra correct, equal (disk-precision) timestamps trigger a recompile. if source.date is not None and source.date >= self.date: if self.md5_for_source.has_key(source_name): if self.md5_for_source[ source_name] == rubber.util.md5_file(source_name): msg.debug(_( "while making %s: contents of %s unchanged, ignoring mtime" ) % (self.products[0], source_name), pkg="depend") continue msg.debug(_( "while making %s: contents of dependency %s changed, rebuilding" ) % (self.products[0], source_name), pkg="depend") return True msg.debug(_( "while making %s: timestamp of dependency %s changed, rebuilding" ) % (self.products[0], source_name), pkg="depend") return True return False
def post_compile(self): """ Run the package-specific operations that are to be performed after each compilation of the main source. Returns true on success or false on failure. """ msg.debug(_("running post-compilation scripts...")) for l in self.onchange: (file, old_contents, cmd) = l new = rubber.contents.snapshot(file) if old_contents != new: # An exception should already have been raised if the # file has disappeared. assert new != rubber.contents.NO_SUCH_FILE l[1] = new msg.info(_("running %s") % cmd) # FIXME portability issue: explicit reference to shell if rubber.util.execute(("sh", "-c", cmd)) != 0: msg.error(_("command '%s' returned a non-zero status"), cmd) return False for mod in self.modules.objects.values(): if not mod.post_compile(): self.failed_module = mod return False return True
def run(self): msg.progress(_("running: %s") % ' '.join(self.command)) process = Popen(self.command, stdin=devnull(), stdout=self.stdout) if process.wait() != 0: msg.error(_("execution of %s failed") % self.command[0]) return False return True
def prepare_source(self, filename): """ Dump the standard input in a file, and set up that file the same way we would normally process LaTeX sources. """ assert filename.endswith("-") # filename is ignored try: # Make a temporary on-disk copy of the standard input, # in the current working directory. # The name will have the form "rubtmpXXX.tex. with tempfile.NamedTemporaryFile(suffix='.tex', prefix='rubtmp', dir='.', delete=False) as srcfile: # note the tempfile name so we can remove it later self.pipe_tempfile = srcfile.name # copy stdin into the tempfile msg.progress(_("saving the input in %s") % self.pipe_tempfile) shutil.copyfileobj(sys.stdin, srcfile) except IOError: msg.error( _("cannot create temporary file for the main LaTeX source")) rubber.util.abort_generic_error() return super(Pipe, self).prepare_source(self.pipe_tempfile)
def display(short, kind, text, **info): """ Print an error or warning message. The argument 'kind' indicates the kind of message, among "error", "warning", "abort", the argument 'text' is the main text of the message, the other arguments provide additional information, including the location of the error. """ if kind == "error": if text[0:13] == "LaTeX Error: ": text = text[13:] msg.warning(rubber.util._format(info, text)) if "code" in info and info["code"] and not short: if "macro" in info: del info["macro"] msg.warning( rubber.util._format(info, _("leading text: ") + info["code"])) elif kind == "abort": if short: m = _("compilation aborted ") + info["why"] else: m = _("compilation aborted: %s %s") % (text, info["why"]) msg.warning(rubber.util._format(info, m)) else: assert kind == "warning" msg.warning(rubber.util._format(info, text))
def prepare_source_pipe(): """ Dump the standard input in a file, and set up that file the same way we would normally process LaTeX sources. """ # FIXME: with a better program structure, the context manager # should remove the input file. try: # Make a temporary on-disk copy of the standard input, # in the current working directory. # The name will have the form "rubtmpXXX.tex. with tempfile.NamedTemporaryFile(suffix='.tex', prefix='rubtmp', dir='.', delete=False) as srcfile: # note the tempfile name so we can remove it later pipe_tempfile = srcfile.name # copy stdin into the tempfile msg.info(_("saving the input in %s") % pipe_tempfile) shutil.copyfileobj(sys.stdin.buffer, srcfile) except IOError: raise rubber.GenericError( _("cannot create temporary file for the main LaTeX source")) return pipe_tempfile
def convert(self, target, prefixes=[""], suffixes=[""], check=None, context=None): """ Use conversion rules to make a given target file, and return: * the file path of an existing file matching the 'target' pattern * the final node of a new dependency tree if a conversion is necessary * None if the file does not exist and cannot be built. The optional arguments 'prefixes' and 'suffixes' are lists of strings that can be added at the beginning and the end of the name when searching for the file. Prefixes are tried in order, and for each prefix, suffixes are tried in order; the first file from this order that exists or can be made is kept. The optional arguments 'check' and 'context' have the same meaning as in 'Converter.best_rule'. """ # Try all suffixes and prefixes until something is found. last = None for t in [p + target + s for s in suffixes for p in prefixes]: # Define a check function, according to preferences. if t in self.conv_prefs: prefs = self.conv_prefs[t] def do_check(vars, prefs=prefs): if prefs is not None: for key, val in prefs.items(): if not (key in vars and vars[key] == val): return 0 return 1 else: prefs = None do_check = check # Find the best applicable rule. ans = self.converter.best_rule(t, check=do_check, context=context) if ans is not None: if last is None or ans["cost"] < last["cost"]: last = ans # Check if the target exists. if prefs is None and os.path.exists(t): if last is not None and last["cost"] <= 0: break msg.debug(_("`%s' is `%s', no rule applied") % (target, t)) return t if last is None: return None msg.debug( _("`%s' is `%s', made from `%s' by rule `%s'") % (target, last["target"], last["source"], last["name"])) return self.converter.apply(last)
def load_cache(cache_path): msg.debug(_('Reading external cache file %s') % cache_path) with open(cache_path) as f: line = f.readline() while line: product = line[:-1] sources = [] snapshots = [] while True: line = f.readline() if not line.startswith(' '): # Including end of file. break limit = 2 + rubber.contents.cs_str_len snapshots.append(rubber.contents.str2cs(line[2:limit])) sources.append(line[limit + 1:-1]) try: node = _producer[product] except KeyError: msg.debug(_('%s: no such recipe anymore') % product) else: if node.sources != sources: msg.debug(_('%s: depends on %s not anymore on %s'), product, " ".join(node.sources), " ".join(sources)) elif node.snapshots is not None: # FIXME: this should not happen. See cweb-latex test. msg.debug(_('%s: rebuilt before cache read'), product) else: msg.debug(_('%s: using cached checksums'), product) node.snapshots = snapshots
def process(self, path): """ This method is called when an included file is processed. The argument must be a valid file name. """ if path in self.processed_sources: msg.debug(_("%s already parsed") % path) return self.processed_sources[path] = None self.add_source(path) try: saved_vars = self.vars.copy() try: msg.debug(_("parsing %s") % path) self.vars["file"] = path self.vars["line"] = None with open(path, encoding='utf_8', errors='replace') as file: self.parse_file(file) finally: self.vars = saved_vars msg.debug(_("end of %s") % path) except EndInput: pass
def run(self): """ This method reads the source file (which is supposed to be a gzip-compressed PostScript file) until it finds a line that contains a bounding box indication. Then it creates the target file with this single line. """ msg.info(_("extracting bounding box from %s") % self.source) # Binary mode causes no decoding errors, but makes # difficult in Python to split UTF-8 input in lines. # Usual TeX encodings and UTF-8 share the property # that we can search for an ASCII pattern, and avoid # data corruption if other bytes are copied unchanged, # whichever character they represent. # Any 8-bit character has the advantage over UTF-8 # that it never causes UnicodeDecodeError. with gzip.open(self.source, mode='rt', encoding='latin_1') as source: for line in source: if re_bbox.match(line): with open(self.target, 'w', encoding='latin_1') as target: target.write(line) return True msg.error(_("no bounding box was found in %s!") % self.source) return False
def readlog(self, name, limit): """ Read the specified log file, checking that it was produced by the right compiler. Returns False if the log file is invalid or does not exist. """ self.lines = None try: with open(name, encoding='utf_8', errors='replace') as fp: line = fp.readline() if not line or not re_loghead.match(line): msg.debug(_('empty log')) return False # do not read the whole log unconditionally whole_file = fp.read(limit) self.lines = whole_file.split('\n') if fp.read(1) != '': # more data to be read msg.warning( _('log file is very long, and will not be read completely.' )) return True except IOError: msg.debug(_('IO Error with log')) return False
def do_tool(self, args): if len(args) != 1: raise rubber.SyntaxError( _("invalid syntax for directive '%s'") % "tool") tool = args[0] if tol not in ("makeindex", "xindy"): msg.error(_("unknown indexing tool '%s'") % tool) self.cmd[0] = tool
def run(self): bak = self.aux + '.away_from_asymptote' msg.debug(_("saving %s to %s"), self.aux, bak) os.rename(self.aux, bak) try: return super().run() finally: msg.debug(_("restoring %s to %s"), bak, self.aux) os.rename(bak, self.aux)
def run(self): msg.info(_("running: %s") % ' '.join(self.command)) process = subprocess.Popen(self.command, stdin=subprocess.DEVNULL, stdout=self.stdout) if process.wait() != 0: msg.error(_("execution of %s failed") % self.command[0]) return False return True
def hook_externaldocument(self, loc, opt, name): aux = self.doc.env.find_file(name + '.aux') if aux: self.doc.add_source(aux) msg.debug(_("dependency %s added for external references") % aux) else: msg.debug( _("file %s.aux is required by xr package but not found") % name)
def run (self): msg.info (_("compressing %s into %s") % (self.source, self.target)) try: with open (self.source, 'rb') as f_in: with self.constructor (self.target, 'wb') as f_out: f_out.writelines (f_in) except: msg.error (_ ("compression failed")) return False return True
def do_onchange(self, args): if len(args) != 2: raise rubber.SyntaxError( _("invalid syntax for directive '%s'") % "onchange") file, cmd = args if self.env.is_in_unsafe_mode_: # A list, because we will update the snapshot later. self.onchange.append([file, rubber.contents.snapshot(file), cmd]) else: msg.warning( _("Rubber directive 'onchange' is valid only in unsafe mode"))
def do_alias(self, args): if len(args) != 2: raise rubber.SyntaxError( _("invalid syntax for directive '%s'") % "alias") name, val = args try: h = self.hooks[val] except KeyError: raise rubber.SyntaxError(_("cannot alias unknown name %s") % val) self.hooks[name] = h self.hooks_version += 1
def real_make(self, force): rv = UNCHANGED patience = 5 primary_product = self.products[0] msg.debug(_("make %s -> %s") % (primary_product, str(self.sources)), pkg="depend") while patience > 0: # make our sources for source_name in self.sources: source = self.set[source_name] if source.making: # cyclic dependency -- drop for now, we will re-visit # this would happen while trying to remake the .aux in order to make the .bbl, for example msg.debug( _("while making %s: cyclic dependency on %s (pruned)") % (primary_product, source_name), pkg="depend") continue source_rv = source.make(force) if source_rv == ERROR: self.failed_dep = source.failed_dep msg.debug( _("while making %s: dependency %s could not be made") % (primary_product, source_name), pkg="depend") return ERROR elif source_rv == CHANGED: rv = CHANGED must_make = force or self.should_make() if not must_make: return rv # record MD5 hash of source files as we now actually start the build for source_name in self.md5_for_source.keys(): self.md5_for_source[source_name] = rubber.util.md5_file( source_name) # actually make if not self.run(): self.failed_dep = self return ERROR self.date = time.time() rv = CHANGED force = False patience -= 1 self.failed_dep = self msg.error(_("while making %s: file contents does not seem to settle") % self.products[0], pkg="depend") return ERROR
def process_source_info(env, act, short): if act == "deps": print(" ".join(env.final.all_leaves())) elif act == "rules": for node in env.final.all_producers(): print("\n%s:" % " ".join(node.products())) print(" ".join(node.sources)) else: # Check for a log file and extract information from it if it exists, # accroding to the argument's value. log = env.main.log if not env.main.parse_log(): raise rubber.GenericError(_("Parsing the log file failed")) if act == "boxes": for err in log.get_boxes(): display(short, **err) else: msg.info(_("There is no bad box.")) elif act == "check": finished = False for err in log.get_errors(): display(short, **err) finished = True if finished: return 0 msg.info(_("There was no error.")) for err in log.get_references(): display(short, **err) finished = True if finished: return 0 msg.info(_("There is no undefined reference.")) for err in log.get_warnings(): display(short, **err) else: msg.info(_("There is no warning.")) for err in log.get_boxes(): display(short, **err) else: msg.info(_("There is no bad box.")) elif act == "errors": for err in log.get_errors(): display(short, **err) else: msg.info(_("There was no error.")) elif act == "refs": for err in log.get_references(): display(short, **err) else: msg.info(_("There is no undefined reference.")) else: assert act == "warnings" for err in log.get_warnings(): display(short, **err) else: msg.info(_("There is no warning."))
def post_compile(self): """ Run makeindex if needed, with appropriate options and environment. """ if not os.path.exists(self.source): msg.log(_("strange, there is no %s") % self.source, pkg="index") return True if not self.run_needed(): return True msg.progress(_("processing index %s") % msg.simplify(self.source)) if self.tool == "makeindex": cmd = ["makeindex", "-q", "-o", self.target] + self.opts cmd.extend(["-t", self.transcript]) if self.style: cmd.extend(["-s", self.style]) cmd.append(self.source) path_var = "INDEXSTYLE" elif self.tool == "xindy": cmd = ["texindy", "--quiet"] for opt in self.opts: if opt == "-g": if self.lang != "": msg.warn(_("'language' overrides 'order german'"), pkg="index") else: self.lang = "german-din" elif opt == "-l": self.modules.append("letter-ordering") msg.warn(_( "use 'module letter-ordering' instead of 'order letter'" ), pkg="index") else: msg.error("unknown option to xindy: %s" % opt, pkg="index") for mod in self.modules: cmd.extend(["--module", mod]) if self.lang: cmd.extend(["--language", self.lang]) cmd.append(self.source) path_var = "XINDY_SEARCHPATH" if self.path != []: env = {path_var: ':'.join(self.path + [os.getenv(path_var, '')])} else: env = {} if self.doc.env.execute(cmd, env): msg.error(_("could not make index %s") % self.target) return False self.doc.must_compile = 1 return True
def do_setlist(self, args): if len(args) == 0: raise rubber.SyntaxError( _("invalid syntax for directive '%s'") % cmd) name, val = args[0], args[1:] if name in ('arguments', ): self.arguments.extend(val) else: msg.warning( rubber.util._format(self.vars, _("unknown list variable: %s") % name))
def post_compile (self): """ Run makeindex if needed, with appropriate options and environment. """ if not os.path.exists(self.source): msg.log(_("strange, there is no %s") % self.source, pkg="index") return True if not self.run_needed(): return True msg.progress(_("processing index %s") % msg.simplify(self.source)) if self.tool == "makeindex": cmd = ["makeindex", "-q", "-o", self.target] + self.opts cmd.extend(["-t", self.transcript]) if self.style: cmd.extend(["-s", self.style]) cmd.append(self.source) path_var = "INDEXSTYLE" elif self.tool == "xindy": cmd = ["texindy", "--quiet"] for opt in self.opts: if opt == "-g": if self.lang != "": msg.warn(_("'language' overrides 'order german'"), pkg="index") else: self.lang = "german-din" elif opt == "-l": self.modules.append("letter-ordering") msg.warn(_("use 'module letter-ordering' instead of 'order letter'"), pkg="index") else: msg.error("unknown option to xindy: %s" % opt, pkg="index") for mod in self.modules: cmd.extend(["--module", mod]) if self.lang: cmd.extend(["--language", self.lang]) cmd.append(self.source) path_var = "XINDY_SEARCHPATH" if self.path != []: env = { path_var: ':'.join(self.path + [os.getenv(path_var, '')]) } else: env = {} if self.doc.env.execute(cmd, env): msg.error(_("could not make index %s") % self.target) return False self.doc.must_compile = 1 return True
def convert (self, target, prefixes=[""], suffixes=[""], check=None, context=None): """ Use conversion rules to make a dependency tree for a given target file, and return the final node, or None if the file does not exist and cannot be built. The optional arguments 'prefixes' and 'suffixes' are lists of strings that can be added at the beginning and the end of the name when searching for the file. Prefixes are tried in order, and for each prefix, suffixes are tried in order; the first file from this order that exists or can be made is kept. The optional arguments 'check' and 'context' have the same meaning as in 'Converter.best_rule'. """ # Try all suffixes and prefixes until something is found. last = None for t in [p + target + s for s in suffixes for p in prefixes]: # Define a check function, according to preferences. if self.conv_prefs.has_key(t): prefs = self.conv_prefs[t] def do_check (vars, prefs=prefs): if prefs is not None: for key, val in prefs.items(): if not (vars.has_key(key) and vars[key] == val): return 0 return 1 else: prefs = None do_check = check # Find the best applicable rule. ans = self.converter.best_rule(t, check=do_check, context=context) if ans is not None: if last is None or ans["cost"] < last["cost"]: last = ans # Check if the target exists. if prefs is None and os.path.exists(t): if last is not None and last["cost"] <= 0: break msg.log(_("`%s' is `%s', no rule applied") % (target, t)) return rubber.depend.Leaf(self.depends, t) if last is None: return None msg.log(_("`%s' is `%s', made from `%s' by rule `%s'") % (target, last["target"], last["source"], last["name"])) return self.converter.apply(last)
def do_rules(self, args): if len(args) != 1: raise rubber.SyntaxError( rubber.util._format( self.vars, _("invalid syntax for directive '%s'") % cmd)) file = args[0] name = self.env.find_file(file) if name is None: msg.warning( rubber.util._format(self.vars, _("cannot read rule file %s") % file)) else: self.env.converter.read_ini(name)
def real_make(self, force): rv = UNCHANGED patience = 5 primary_product = self.products[0] msg.debug(_("make %s -> %s") % (primary_product, str(self.sources)), pkg="depend") while patience > 0: # make our sources for source_name in self.sources: source = self.set[source_name] if source.making: # cyclic dependency -- drop for now, we will re-visit # this would happen while trying to remake the .aux in order to make the .bbl, for example msg.debug( _("while making %s: cyclic dependency on %s (pruned)") % (primary_product, source_name), pkg="depend", ) continue source_rv = source.make(force) if source_rv == ERROR: self.failed_dep = source.failed_dep msg.debug( _("while making %s: dependency %s could not be made") % (primary_product, source_name), pkg="depend", ) return ERROR elif source_rv == CHANGED: rv = CHANGED must_make = force or self.should_make() if not must_make: return rv # record MD5 hash of source files as we now actually start the build for source_name in self.md5_for_source.keys(): self.md5_for_source[source_name] = rubber.util.md5_file(source_name) # actually make if not self.run(): self.failed_dep = self return ERROR self.date = time.time() rv = CHANGED force = False patience -= 1 self.failed_dep = self msg.error(_("while making %s: file contents does not seem to settle") % self.products[0], pkg="depend") return ERROR
def main (self, cmdline): self.env = Environment() self.prologue = [] self.epilogue = [] self.act = None args = self.parse_opts(cmdline) if not self.act: self.act = "check" msg.log(_( "This is Rubber's information extractor version %s.") % version) if len(args) != 1: sys.stderr.write(_("You must specify one source file.\n")) sys.exit(1) src = args[0] if self.env.set_source(src): sys.stderr.write(_("I cannot find %s.\n") % src) sys.exit(1) if self.act == "deps": self.prepare(src) deps = {} for dep in self.env.main.source_nodes(): for file in dep.leaves(): deps[file] = None print(' '.join(deps.keys())) elif self.act == "rules": self.prepare(src) seen = {} next_ = [self.env.final] while len(next_) > 0: node = next_[0] next_ = next_[1:] if seen.has_key(node): continue seen[node] = None if len(node.sources) == 0: continue print("\n%s:" % ' '.join(node.products), end=',') print(' '.join(node.sources)) next_.extend(node.source_nodes()) else: self.prepare(src, parse=0) return self.info_log(self.act) return 0
def main(self, cmdline): self.env = Environment() self.prologue = [] self.epilogue = [] self.act = None args = self.parse_opts(cmdline) if not self.act: self.act = "check" msg.log( _("This is Rubber's information extractor version %s.") % version) if len(args) != 1: sys.stderr.write(_("You must specify one source file.\n")) sys.exit(1) src = args[0] if self.env.set_source(src): sys.stderr.write(_("I cannot find %s.\n") % src) sys.exit(1) if self.act == "deps": self.prepare(src) deps = {} for dep in self.env.main.source_nodes(): for file in dep.leaves(): deps[file] = None print(' '.join(deps.keys())) elif self.act == "rules": self.prepare(src) seen = {} next_ = [self.env.final] while len(next_) > 0: node = next_[0] next_ = next_[1:] if seen.has_key(node): continue seen[node] = None if len(node.sources) == 0: continue print("\n%s:" % ' '.join(node.products), end=',') print(' '.join(node.sources)) next_.extend(node.source_nodes()) else: self.prepare(src, parse=0) return self.info_log(self.act) return 0
def do_make(self, args): if len(args) % 2 != 1: raise rubber.SyntaxError( _("invalid syntax for directive '%s'") % "make") file = args[0] vars = {"target": file} for i in range(1, len(args), 2): if args[i] == "from": vars["source"] = args[i + 1] elif args[i] == "with": vars["name"] = args[1 + 1] else: raise rubber.SyntaxError( _("invalid syntax for directive '%s'") % "make") self.env.conv_set(file, vars)
def run(self): # command might have been updated in the mean time, so get it now self.environ["BIBINPUTS"] = ":".join(self.bib_paths) self.environ["BSTINPUTS"] = ":".join(self.bst_paths) command = self.build_command() msg.info(_("running: %s") % " ".join(command)) process = subprocess.Popen(command, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, env=self.environ) if process.wait() != 0: msg.error(_("There were errors running %s.") % self.tool) return False return True
def get_errors(self): """ Read the log file, identify error messages and report them. """ if self.tool != "biber": # we re-use the BibTeX support in superclass for error in super(BibLaTeXDep, self).get_errors(): yield error return current_bib = None try: log = open(self.blg, "r") except: msg.warn(_("cannot open Biber logfile: %s") % self.blg, pkg="biblatex") return with log: for line in log: m = re_updatefile.match(line) if m: current_bib = m.group("filename") m = re_error.match(line) if m: d = {"pkg": "biber"} d["kind"] = biber_to_rubber[m.group("kind")] if current_bib: d["file"] = current_bib d["line"] = int(m.group("line")) d["text"] = m.group("text") yield d
def do_order (self, *args): for opt in args: if opt == "standard": self.opts = [] elif opt == "german": self.opts.append("-g") elif opt == "letter": self.opts.append("-l") else: msg.warn( _("unknown option '%s' for 'makeidx.order'") % opt)
def set_source (self, name, jobname=None): """ Create a main dependency node from the given file name. If this name has an extension that is known of a preprocessor, this preprocessor is used, otherwise the name is that of a LaTeX source. """ path = self.find_file(name, ".tex") if not path: msg.error(_("cannot find %s") % name) return 1 base,ext = os.path.splitext(path) if ext in literate_preprocessors.keys(): src = base + ".tex" self.src_node = literate_preprocessors[ext](self.depends, src, path) else: src = path self.src_node = None from rubber.converters.latex import LaTeXDep self.main = LaTeXDep(self) if os.path.exists(src): if self.main.set_source(src, jobname): return 1 self.final = self.main return 0
def run (self): source = self.sources [0] if not os.path.exists (source): msg.info(_("{} not yet generated").format (msg.simplify (source)), pkg="asymptote") return True os.rename (self.aux, self.bak) msg.log (_ ("saving {} to {}").format (msg.simplify (self.aux), msg.simplify (self.bak)), pkg="asymptote") ret = super (Shell_Restoring_Aux, self).run () msg.log (_ ("restoring {} to {}").format (msg.simplify (self.aux), msg.simplify (self.bak)), pkg="asymptote") os.rename (self.bak, self.aux) return ret
def help (self): sys.stderr.write (_("""\ This is Rubber version %s. usage: rubber-pipe [options] available options: -b, --bzip2 compress the final document with bzip2 -c, --command=CMD run the directive CMD before parsing (see man page) -e, --epilogue=CMD run the directive CMD after parsing -z, --gzip compress the final document -h, --help display this help --into=DIR go to directory DIR before compiling -k, --keep keep the temporary files after compiling -n, --maxerr=NUM display at most NUM errors (default: 10) -m, --module=MOD[:OPTS] use module MOD (with options OPTS) --only=SOURCES only include the specified SOURCES -o, --post=MOD[:OPTS] postprocess with module MOD (with options OPTS) -d, --pdf produce a pdf (synonym for -m pdftex or -o ps2pdf) -p, --ps process through dvips (synonym for -m dvips) -q, --quiet suppress messages -r, --read=FILE read additional directives from FILE -S, --src-specials enable insertion of source specials -s, --short display errors in a compact form -I, --texpath=DIR add DIR to the search path for LaTeX -v, --verbose increase verbosity --version print version information and exit """) % rubber_version)
def do_shell_escape(self, args): if len(args) != 0: raise rubber.SyntaxError( rubber.util._format( self.vars, _("invalid syntax for directive '%s'") % cmd)) self.env.doc_requires_shell_ = True
def help (self): """ Display the description of all the options and exit. """ sys.stderr.write (_("""\ This is Rubber version %s. usage: rubber [options] sources... available options: -b, --bzip2 compress the final document with bzip2 --clean remove produced files instead of compiling -c, --command=CMD run the directive CMD before parsing (see man page) -e, --epilogue=CMD run the directive CMD after parsing -f, --force force at least one compilation -z, --gzip compress the final document -h, --help display this help --inplace compile the documents from their source directory --into=DIR go to directory DIR before compiling --jobname=NAME set the job name for the first target -n, --maxerr=NUM display at most NUM errors (default: 10) -m, --module=MOD[:OPTS] use module MOD (with options OPTS) --only=SOURCES only include the specified SOURCES -o, --post=MOD[:OPTS] postprocess with module MOD (with options OPTS) -d, --pdf produce a pdf (synonym for -m pdftex or -o ps2pdf) -p, --ps process through dvips (synonym for -o dvips) -q, --quiet suppress messages -r, --read=FILE read additional directives from FILE -S, --src-specials enable insertion of source specials --synctex enable SyncTeX support --unsafe permits the document to run external commands -s, --short display errors in a compact form -I, --texpath=DIR add DIR to the search path for LaTeX -v, --verbose increase verbosity --version print version information and exit -W, --warn=TYPE report warnings of the given TYPE (see man page) """) % rubber.version.version)
def clean (self): """ Remove all generated files related to the index. """ for file in self.source, self.target, self.transcript: if os.path.exists(file): msg.log(_("removing %s") % file, pkg="index") os.unlink(file)
def add_bib_resource(self, doc, opt, name): msg.log(_("bibliography resource discovered: %s" % name), pkg="biblio") options = rubber.util.parse_keyval(opt) # If the file name looks like it contains a control sequence # or a macro argument, forget about this resource. if name.find("\\") > 0 or name.find("#") > 0: return # skip Biber remote resources if "location" in options and options["location"] == "remote": return filename = self.find_bib(name) if filename is None: msg.error(_("cannot find bibliography resource %s") % name, pkg="biblatex") else: self.add_source(filename)
def short_help (self): """ Display a short description of the command line. """ sys.stderr.write (_("""\ usage: rubber [options] sources... For more information, try `rubber --help'. """)) rubber.util.abort_rubber_syntax_error ()
def __call__ (self, cmdline): if cmdline == []: self.short_help() return 1 try: self.main(cmdline) except KeyboardInterrupt: msg(0, _("*** interrupted")) return 2
def run (self): """ Run Metapost from the source file's directory, so that figures are put next to their source file. """ msg.progress(_("running Metapost on %s") % msg.simplify(self.base + ".mp")) if self.env.execute (self.cmd, self.penv, pwd=self.cmd_pwd) == 0: return True # This creates a log file that has the same aspect as TeX logs. self.log = MPLogCheck(self.cmd_pwd) if not self.log.readlog (self.base + ".log", metapost_logfile_limit): msg.error(_( "I can't read MetaPost's log file, this is wrong.")) return False return not self.log.errors()
def run_needed (self): """ Check if makeindex has to be run. This is the case either if the target file does not exist or if the source file has changed. """ if not os.path.exists(self.target): self.md5 = md5_file(self.source) return 1 if not self.md5: self.md5 = md5_file(self.source) msg.log(_("the index file %s is new") % self.source, pkg="index") return 1 new = md5_file(self.source) if self.md5 == new: msg.log(_("the index %s did not change") % self.source, pkg="index") return 0 self.md5 = new msg.log(_("the index %s has changed") % self.source, pkg="index") return 1
def __call__ (self, cmdline): """ This method is a wrapper around the main method, catching the keyboard interruption signal. """ try: self.main (cmdline) assert False except KeyboardInterrupt: msg(0, _("*** interrupted")) rubber.util.abort_generic_error ()
def parse_kpse (): for line in process.stderr.readlines(): line = line.rstrip() match = re_kpse.match(line) if not match: continue cmd = match.group("cmd") if self.kpse_msg.has_key(cmd): msg.progress(match.expand(self.kpse_msg[cmd])) else: msg.progress(_("kpathsea running %s") % cmd)
def clean (self): """ Remove the files produced by this rule and recursively clean all dependencies. """ for file in self.products: if os.path.exists(file): msg.log(_("removing %s") % file) os.unlink(file) for source in self.source_nodes(): source.clean() self.date = None
def execute (self, prog, env={}, pwd=None, out=None): """ Silently execute an external program. The `prog' argument is the list of arguments for the program, `prog[0]' is the program name. The `env' argument is a dictionary with definitions that should be added to the environment when running the program. The standard output is passed line by line to the `out' function (or discarded by default). """ msg.info(_("executing: %s") % string.join(prog)) if pwd: msg.log(_(" in directory %s") % pwd) if env != {}: msg.log(_(" with environment: %r") % env) progname = prog_available(prog[0]) if not progname: msg.error(_("%s not found") % prog[0]) return 1 penv = os.environ.copy() for (key,val) in env.items(): penv[key] = val process = Popen(prog, executable = progname, env = penv, cwd = pwd, stdin = devnull(), stdout = subprocess.PIPE, stderr = None) if out is not None: for line in process.stdout: out(line) else: process.stdout.readlines() ret = process.wait() msg.log(_("process %d (%s) returned %d") % (process.pid, prog[0],ret)) return ret
def build (self, env): """ Build the final product. """ srcname = env.main.sources[0] # FIXME unindent, untangle if True: if self.force: ret = env.main.make(True) if ret != ERROR and env.final is not env.main: ret = env.final.make() else: # This is a hack for the call to get_errors() below # to work when compiling failed when using -f. env.final.failed_dep = env.main.failed_dep else: ret = env.final.make(self.force) if ret == ERROR: msg.info(_("There were errors compiling %s.") % srcname) number = self.max_errors for err in env.final.failed().get_errors(): if number == 0: msg.info(_("More errors.")) break msg.display(**err) number -= 1 rubber.util.abort_generic_error () if ret == UNCHANGED: msg(1, _("nothing to be done for %s") % srcname) if self.warn: # FIXME log = env.main.log if not env.main.parse_log (): msg.error(_("cannot read the log file")) return 1 msg.display_all(log.parse(boxes=self.warn_boxes, refs=self.warn_refs, warnings=self.warn_misc))
def __init__ (self, document, context): env = document.env if env.final.products[0][-3:] != '.ps': msg.error(_("I can't use ps2pdf when not producing a PS")) rubber.util.abort_generic_error () ps = env.final.products[0] pdf = ps[:-2] + 'pdf' cmd = ['ps2pdf'] cmd.extend([ps, pdf]) dep = Shell (env.depends, cmd) dep.add_product (pdf) dep.add_source (ps) env.final = dep
def prepare_source (self, filename): """ Dump the standard input in a file, and set up that file the same way we would normally process LaTeX sources. """ assert filename.endswith ("-") # filename is ignored try: # Make a temporary on-disk copy of the standard input, # in the current working directory. # The name will have the form "rubtmpXXX.tex. with tempfile.NamedTemporaryFile (suffix='.tex', prefix='rubtmp', dir='.', delete=False) as srcfile: # note the tempfile name so we can remove it later self.pipe_tempfile = srcfile.name # copy stdin into the tempfile msg.progress (_("saving the input in %s") % self.pipe_tempfile) shutil.copyfileobj (sys.stdin, srcfile) except IOError: msg.error (_("cannot create temporary file for the main LaTeX source")) rubber.util.abort_generic_error () return super (Pipe, self).prepare_source (self.pipe_tempfile)
def __init__ (self, cwd=None): """ Initialize the environment. The optional argument is the path to the reference directory for compilation, by default it is the current working directory. """ self.kpse_msg = { "mktextfm" : _("making font metrics for \\g<arg>"), "mktexmf" : _("making font \\g<arg>"), "mktexpk" : _("making bitmap for font \\g<arg>") } if cwd is None: cwd = os.getcwd() self.vars = Variables(items = { 'cwd': cwd, '_environment': self }) self.path = [cwd] self.conv_prefs = {} self.depends = rubber.depend.Set() self.converter = Converter(self.depends) self.converter.read_ini(os.path.join(moddir, 'rules.ini')) self.main = None self.final = None
def prepare_source (self, filename): """ Prepare the dependency node for the main LaTeX run. Returns the filename of the main LaTeX source file, which might change for various reasons (adding a .tex suffix; preprocessors; pipe dumping). When this is done, the file must exist on disk, otherwise this function must exit(1) or exit(2). """ path = rubber.util.find_resource (filename, suffix=".tex") if not path: msg.error (_("Main document not found: '%s'") % filename) rubber.util.abort_generic_error () base, ext = os.path.splitext (path) from rubber.converters.literate import literate_preprocessors as lpp if ext in lpp.keys (): src = base + ".tex" # FIXME kill src_node src_node = lpp[ext] (self.env.depends, src, path) if self.rubber_mode == "build": if not self.unsafe: msg.error (_("Running external commands requires --unsafe.")) rubber.util.abort_rubber_syntax_error () # Produce the source from its dependency rules, if needed. if src_node.make () == ERROR: msg.error (_("Producing the main LaTeX file failed: '%s'") \ % src) rubber.util.abort_generic_error () else: src = path from rubber.converters.latex import LaTeXDep self.env.final = self.env.main = LaTeXDep (self.env, src, self.jobname) return src
def run (self): if not os.path.exists (self.cmd [1]): msg.info (_ ("%s not yet generated" % self.cmd [1])) return True # No more settings are expected, we compute the # command once and for all. if self.command_env == None: if self.cmd [0] == "makeindex": self.cmd.extend (self.opts) if self.style: self.cmd.extend (["-s", self.style]) path_var = "INDEXSTYLE" else: # self.cmd [0] == "texindy" for opt in self.opts: if opt == "-g": if self.lang != "": msg.warn(_("'language' overrides 'order german'"), pkg="index") else: self.lang = "german-din" else: # opt == "-l" self.modules.append("letter-ordering") msg.warn(_("use 'module letter-ordering' instead of 'order letter'"), pkg="index") for mod in self.modules: self.cmd.extend(["--module", mod]) if self.lang: self.cmd.extend(["--language", self.lang]) path_var = "XINDY_SEARCHPATH" if self.path != []: self.command_env = { path_var: ':'.join(self.path + [os.getenv(path_var, '')]) } else: self.command_env = {} # The actual run. return self.doc.env.execute(self.cmd, self.command_env) == 0
def set_source (self, name, jobname=None): """ Create a main dependency node from the given file name. If this name has an extension that is known of a preprocessor, this preprocessor is used, otherwise the name is that of a LaTeX source. """ src = None i = name.rfind(".") if i >= 0: ext = name[i+1:] if ext in ["w", "lhs"]: path = self.find_file(name) if not path: msg.error(_("cannot find %s") % name) return 1 src = path[:-len(ext)] + "tex" if ext == "w": from rubber.converters.cweb import CWebDep self.src_node = CWebDep(self, src, path) elif ext == "lhs": from rubber.converters.lhs2TeX import LHSDep self.src_node = LHSDep(self, src, path) if src is None: path = self.find_file(name, ".tex") if not path: msg.error(_("cannot find %s") % name) return 1 src = path self.src_node = None from rubber.converters.latex import LaTeXDep self.main = LaTeXDep(self) if os.path.exists(src): if self.main.set_source(src, jobname): return 1 self.final = self.main return 0
def __init__(self, document, context): doc = document options = rubber.util.parse_keyval(context["opt"]) backend = options.setdefault("backend", "biber") if backend not in ("biber", "bibtex", "bibtex8", "bibtexu"): msg.error(_("Garbled biblatex backend: backend=%s (aborting)") % backend) rubber.util.abort_generic_error() # abort rather than guess self.dep = BibLaTeXDep(doc, backend) doc.hook_macro("bibliography", "a", self.dep.add_bibliography) # overwrite the hook which would load the bibtex module doc.hook_macro("bibliographystyle", "a", self.dep.bibliographystyle)