X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FLaTeX.cpp;h=f5b6d20d3029f42add5f1207e9e3f6195c542f9b;hb=89d9334e03c311a4a7585f40ad81880304d174d4;hp=e68e07c71f921807ebb1bcee5b5348cb01657cee;hpb=2a8c8b6345ce7c3abd307c2d449380a1cac4b6e8;p=lyx.git diff --git a/src/LaTeX.cpp b/src/LaTeX.cpp index e68e07c71f..f5b6d20d30 100644 --- a/src/LaTeX.cpp +++ b/src/LaTeX.cpp @@ -15,11 +15,15 @@ #include +#include "Buffer.h" #include "BufferList.h" +#include "BufferParams.h" #include "LaTeX.h" #include "LyXRC.h" #include "LyX.h" #include "DepTable.h" +#include "Encoding.h" +#include "Language.h" #include "support/debug.h" #include "support/convert.h" @@ -105,7 +109,7 @@ LaTeX::LaTeX(string const & latex, OutputParams const & rp, FileName const & f, string const & p, string const & lp, bool allow_cancellation, bool const clean_start) : cmd(latex), file(f), path(p), lpath(lp), runparams(rp), biber(false), - allow_cancel(allow_cancellation) + allow_cancel(allow_cancellation) { num_errors = 0; // lualatex can still produce a DVI with --output-format=dvi. However, @@ -161,6 +165,10 @@ void LaTeX::removeAuxiliaryFiles() const FileName const gls(changeExtension(file.absFileName(), ".gls")); gls.removeFile(); + // endnotes file + FileName const ent(changeExtension(file.absFileName(), ".ent")); + ent.removeFile(); + // Also remove the aux file FileName const aux(changeExtension(file.absFileName(), ".aux")); aux.removeFile(); @@ -183,6 +191,7 @@ int LaTeX::run(TeXErrors & terr) { int scanres = NO_ERRORS; int bscanres = NO_ERRORS; + int iscanres = NO_ERRORS; unsigned int count = 0; // number of times run num_errors = 0; // just to make sure. unsigned int const MAX_RUN = 6; @@ -220,6 +229,25 @@ int LaTeX::run(TeXErrors & terr) } if (had_depfile) { + if (runparams.includeall) { + // On an "includeall" call (whose purpose is to set up/maintain counters and references + // for includeonly), we remove the master from the dependency list since + // (1) it will be checked anyway on the subsequent includeonly run + // (2) the master is always changed (due to the \includeonly line), and this alone would + // trigger a complete, expensive run each time + head.remove_file(file); + // Also remove all children which are included + Buffer const * buf = theBufferList().getBufferFromTmp(file.absFileName()); + if (buf && buf->params().maintain_unincluded_children == BufferParams::CM_Mostly) { + for (auto const & incfile : buf->params().getIncludedChildren()) { + string const incm = + DocFileName(changeExtension(makeAbsPath(incfile, path) + .absFileName(), ".tex")).mangledFileName(); + FileName inc = makeAbsPath(incm, file.onlyPath().realPath()); + head.remove_file(inc); + } + } + } // Update the checksums head.update(); // Can't just check if anything has changed because it might @@ -254,16 +282,16 @@ int LaTeX::run(TeXErrors & terr) message(runMessage(count)); int exit_code = startscript(); - if (exit_code == Systemcall::KILLED) - return Systemcall::KILLED; + if (exit_code == Systemcall::KILLED || exit_code == Systemcall::TIMEOUT) + return exit_code; scanres = scanLogFile(terr); if (scanres & ERROR_RERUN) { LYXERR(Debug::LATEX, "Rerunning LaTeX"); terr.clearErrors(); exit_code = startscript(); - if (exit_code == Systemcall::KILLED) - return Systemcall::KILLED; + if (exit_code == Systemcall::KILLED || exit_code == Systemcall::TIMEOUT) + return exit_code; scanres = scanLogFile(terr); } @@ -295,8 +323,11 @@ int LaTeX::run(TeXErrors & terr) // onlyFileName() is needed for cygwin int const ret = runMakeIndex(onlyFileName(idxfile.absFileName()), runparams); - if (ret == Systemcall::KILLED) - return Systemcall::KILLED; + if (ret == Systemcall::KILLED || ret == Systemcall::TIMEOUT) + return ret; + FileName const ilgfile(changeExtension(file.absFileName(), ".ilg")); + if (ilgfile.exists()) + iscanres = scanIlgFile(terr); rerun = true; } @@ -308,8 +339,8 @@ int LaTeX::run(TeXErrors & terr) // FIXME: Sort out the real problem in DepTable. if (head.haschanged(nlofile) || (nlofile.exists() && nlofile.isFileEmpty())) { int const ret = runMakeIndexNomencl(file, ".nlo", ".nls"); - if (ret == Systemcall::KILLED) - return Systemcall::KILLED; + if (ret == Systemcall::KILLED || ret == Systemcall::TIMEOUT) + return ret; rerun = true; } @@ -330,7 +361,12 @@ int LaTeX::run(TeXErrors & terr) // run bibtex // if (scanres & UNDEF_CIT || scanres & RERUN || run_bibtex) - if (scanres & UNDEF_CIT || run_bibtex) { + // We do not run bibtex/biber on an "includeall" call (whose purpose is + // to set up/maintain counters and references for includeonly) since + // (1) bibliographic references will be updated on the subsequent includeonly run + // (2) this would trigger a complete run each time (as references in non-included + // children are removed on subsequent includeonly runs) + if (!runparams.includeall && (scanres & UNDEF_CIT || run_bibtex)) { // Here we must scan the .aux file and look for // "\bibdata" and/or "\bibstyle". If one of those // tags is found -> run bibtex and set rerun = true; @@ -340,8 +376,8 @@ int LaTeX::run(TeXErrors & terr) updateBibtexDependencies(head, bibtex_info); int exit_code; rerun |= runBibTeX(bibtex_info, runparams, exit_code); - if (exit_code == Systemcall::KILLED) - return Systemcall::KILLED; + if (exit_code == Systemcall::KILLED || exit_code == Systemcall::TIMEOUT) + return exit_code; FileName const blgfile(changeExtension(file.absFileName(), ".blg")); if (blgfile.exists()) bscanres = scanBlgFile(head, terr); @@ -371,8 +407,8 @@ int LaTeX::run(TeXErrors & terr) LYXERR(Debug::LATEX, "Run #" << count); message(runMessage(count)); int exit_code = startscript(); - if (exit_code == Systemcall::KILLED) - return Systemcall::KILLED; + if (exit_code == Systemcall::KILLED || exit_code == Systemcall::TIMEOUT) + return exit_code; scanres = scanLogFile(terr); // update the depedencies @@ -386,7 +422,12 @@ int LaTeX::run(TeXErrors & terr) // rerun bibtex? // Complex bibliography packages such as Biblatex require // an additional bibtex cycle sometimes. - if (scanres & UNDEF_CIT) { + // We do not run bibtex/biber on an "includeall" call (whose purpose is + // to set up/maintain counters and references for includeonly) since + // (1) bibliographic references will be updated on the subsequent includeonly run + // (2) this would trigger a complete run each time (as references in non-included + // children are removed on subsequent includeonly runs) + if (!runparams.includeall && scanres & UNDEF_CIT) { // Here we must scan the .aux file and look for // "\bibdata" and/or "\bibstyle". If one of those // tags is found -> run bibtex and set rerun = true; @@ -396,8 +437,8 @@ int LaTeX::run(TeXErrors & terr) updateBibtexDependencies(head, bibtex_info); int exit_code; rerun |= runBibTeX(bibtex_info, runparams, exit_code); - if (exit_code == Systemcall::KILLED) - return Systemcall::KILLED; + if (exit_code == Systemcall::KILLED || exit_code == Systemcall::TIMEOUT) + return exit_code; FileName const blgfile(changeExtension(file.absFileName(), ".blg")); if (blgfile.exists()) bscanres = scanBlgFile(head, terr); @@ -419,9 +460,12 @@ int LaTeX::run(TeXErrors & terr) // onlyFileName() is needed for cygwin int const ret = runMakeIndex(onlyFileName(changeExtension( file.absFileName(), ".idx")), runparams); - if (ret == Systemcall::KILLED) - return Systemcall::KILLED; - rerun = true; + if (ret == Systemcall::KILLED || ret == Systemcall::TIMEOUT) + return ret; + FileName const ilgfile(changeExtension(file.absFileName(), ".ilg")); + if (ilgfile.exists()) + iscanres = scanIlgFile(terr); + rerun = true; } // MSVC complains that bool |= int is unsafe. Not sure why. @@ -470,6 +514,9 @@ int LaTeX::run(TeXErrors & terr) if (bscanres & ERRORS) return bscanres; // return on error + if (iscanres & ERRORS) + return iscanres; // return on error + return scanres; } @@ -496,11 +543,50 @@ int LaTeX::runMakeIndex(string const & f, OutputParams const & rp, if (!rp.index_command.empty()) tmp = rp.index_command; + Language const * doc_lang = languages.getLanguage(rp.document_language); + + if (contains(tmp, "$$x")) { + // This adds appropriate [te]xindy options + // such as language and codepage (for the + // main document language/encoding) as well + // as input markup (latex or xelatex) + string xdyopts = doc_lang ? doc_lang->xindy() : string(); + if (!xdyopts.empty()) + xdyopts = "-L " + xdyopts; + if (rp.isFullUnicode() && rp.encoding->package() == Encoding::none) { + if (!xdyopts.empty()) + xdyopts += " "; + // xelatex includes lualatex + xdyopts += "-I xelatex"; + } + else if (rp.encoding->iconvName() == "UTF-8") { + if (!xdyopts.empty()) + xdyopts += " "; + // -I not really needed for texindy, but for xindy + xdyopts += "-C utf8 -I latex"; + } + else { + if (!xdyopts.empty()) + xdyopts += " "; + // not really needed for texindy, but for xindy + xdyopts += "-I latex"; + } + tmp = subst(tmp, "$$x", xdyopts); + } + + if (contains(tmp, "$$b")) { + // advise xindy to write a log file + tmp = subst(tmp, "$$b", removeExtension(f)); + } + LYXERR(Debug::LATEX, "idx file has been made, running index processor (" << tmp << ") on file " << f); - tmp = subst(tmp, "$$lang", rp.document_language); + if (doc_lang) { + tmp = subst(tmp, "$$lang", doc_lang->babel()); + tmp = subst(tmp, "$$lcode", doc_lang->code()); + } if (rp.use_indices) { tmp = lyxrc.splitindex_command + " -m " + quoteName(tmp); LYXERR(Debug::LATEX, @@ -715,6 +801,7 @@ int LaTeX::scanLogFile(TeXErrors & terr) bool fle_style = false; static regex const file_line_error(".+\\.\\D+:[0-9]+: (.+)"); static regex const child_file("[^0-9]*([0-9]+[A-Za-z]*_.+\\.tex).*"); + static regex const undef_ref(".*Reference `(\\w+)\\' on page.*"); // Flag for 'File ended while scanning' message. // We need to wait for subsequent processing. string wait_for_error; @@ -726,6 +813,7 @@ int LaTeX::scanLogFile(TeXErrors & terr) terr.clearRefs(); string token; + string ml_token; while (getline(ifs, token)) { // MikTeX sometimes inserts \0 in the log file. They can't be // removed directly with the existing string utility @@ -740,6 +828,9 @@ int LaTeX::scanLogFile(TeXErrors & terr) if (token.empty()) continue; + if (!ml_token.empty()) + ml_token += token; + // Track child documents for (size_t i = 0; i < token.length(); ++i) { if (token[i] == '(') { @@ -777,11 +868,14 @@ int LaTeX::scanLogFile(TeXErrors & terr) //TODO: TL 2020 engines will contain new commandline switch --cnf-line which we //can use to set max_print_line variable for appropriate length and detect all //errors correctly. - if (contains(token, "There were undefined citations.")) + if (!runparams.includeall && (contains(token, "There were undefined citations.") || + prefixIs(token, "Package biblatex Warning: The following entry could not be found"))) retval |= UNDEF_CIT; - if (prefixIs(token, "LaTeX Warning:") || - prefixIs(token, "! pdfTeX warning")) { + if (prefixIs(token, "LaTeX Warning:") + || prefixIs(token, "! pdfTeX warning") + || prefixIs(ml_token, "LaTeX Warning:") + || prefixIs(ml_token, "! pdfTeX warning")) { // Here shall we handle different // types of warnings retval |= LATEX_WARNING; @@ -799,26 +893,65 @@ int LaTeX::scanLogFile(TeXErrors & terr) } else if (contains(token, "Etaremune labels have changed")) { retval |= ERROR_RERUN; LYXERR(Debug::LATEX, "Force rerun."); + // package enotez + } else if (contains(token, "Endnotes may have changed. Rerun")) { + retval |= RERUN; + LYXERR(Debug::LATEX, "We should rerun."); //"Citation `cit' on page X undefined on input line X." - } else if (contains(token, "Citation") + } else if (!runparams.includeall && contains(token, "Citation") //&& contains(token, "on input line") //often split to newline && contains(token, "undefined")) { retval |= UNDEF_CIT; terr.insertRef(getLineNumber(token), from_ascii("Citation undefined"), from_utf8(token), child_name); //"Reference `X' on page Y undefined on input line Z." - } else if (contains(token, "Reference") - //&& contains(token, "on input line")) //often split to new line + // This warning might be broken accross multiple lines with long labels. + // Thus we check that + } else if (contains(token, "Reference `") && !contains(token, "on input line")) { + // Rest of warning in next line(s) + // Save to ml_token + ml_token = token; + } else if (!ml_token.empty() && contains(ml_token, "Reference `") + && !contains(ml_token, "on input line")) { + // not finished yet. Continue with next line. + continue; + } else if (!ml_token.empty() && contains(ml_token, "Reference `") + && contains(ml_token, "on input line")) { + // We have collected the whole warning now. + if (!contains(ml_token, "undefined")) { + // Not the warning we are looking for + ml_token.clear(); + continue; + } + if (regex_match(ml_token, sub, undef_ref)) { + string const ref = sub.str(1); + Buffer const * buf = theBufferList().getBufferFromTmp(file.absFileName()); + if (!buf || !buf->masterBuffer()->activeLabel(from_utf8(ref))) { + terr.insertRef(getLineNumber(ml_token), from_ascii("Reference undefined"), + from_utf8(ml_token), child_name); + retval |= UNDEF_UNKNOWN_REF; + } + } + ml_token.clear(); + retval |= UNDEF_REF; + } else if (contains(token, "Reference `") + && contains(token, "on input line") && contains(token, "undefined")) { + if (regex_match(token, sub, undef_ref)) { + string const ref = sub.str(1); + Buffer const * buf = theBufferList().getBufferFromTmp(file.absFileName()); + if (!buf || !buf->masterBuffer()->activeLabel(from_utf8(ref))) { + terr.insertRef(getLineNumber(token), from_ascii("Reference undefined"), + from_utf8(token), child_name); + retval |= UNDEF_UNKNOWN_REF; + } + } retval |= UNDEF_REF; - terr.insertRef(getLineNumber(token), from_ascii("Reference undefined"), - from_utf8(token), child_name); - - //If label is too long pdlaftex log line splitting will make the above fail - //so we catch at least this generic statement occuring for both CIT & REF. - } else if (contains(token, "There were undefined references.")) { + // In case the above checks fail we catch at least this generic statement + // occuring for both CIT & REF. + } else if (!runparams.includeall && contains(token, "There were undefined references.")) { if (!(retval & UNDEF_CIT)) //if not handled already - retval |= UNDEF_REF; + retval |= UNDEF_REF; } } else if (prefixIs(token, "Package")) { @@ -826,7 +959,8 @@ int LaTeX::scanLogFile(TeXErrors & terr) retval |= PACKAGE_WARNING; if (contains(token, "natbib Warning:")) { // Natbib warnings - if (contains(token, "Citation") + if (!runparams.includeall + && contains(token, "Citation") && contains(token, "on page") && contains(token, "undefined")) { retval |= UNDEF_CIT; @@ -834,9 +968,9 @@ int LaTeX::scanLogFile(TeXErrors & terr) terr.insertRef(getLineNumber(token), from_ascii("Citation undefined"), from_utf8(token), child_name); } - } else if (contains(token, "run BibTeX")) { + } else if (!runparams.includeall && contains(token, "run BibTeX")) { retval |= UNDEF_CIT; - } else if (contains(token, "run Biber")) { + } else if (!runparams.includeall && contains(token, "run Biber")) { retval |= UNDEF_CIT; biber = true; } else if (contains(token, "Rerun LaTeX") || @@ -923,10 +1057,15 @@ int LaTeX::scanLogFile(TeXErrors & terr) // get the next line int count = 0; + // We also collect intermediate lines + // This is needed for errors in preamble + string intermediate; do { if (!getline(ifs, tmp)) break; tmp = rtrim(tmp, "\r"); + if (!prefixIs(tmp, "l.")) + intermediate += tmp; // 15 is somewhat arbitrarily chosen, based on practice. // We used 10 for 14 years and increased it to 15 when we // saw one case. @@ -948,6 +1087,15 @@ int LaTeX::scanLogFile(TeXErrors & terr) sscanf(tmp.c_str(), "l.%d", &line); // get the rest of the message: string errstr(tmp, tmp.find(' ')); + bool preamble_error = false; + if (suffixIs(errstr, "\\begin{document}")) { + // this is an error in preamble + // the real error is in the + // intermediate lines + errstr = intermediate; + tmp = intermediate; + preamble_error = true; + } errstr += '\n'; getline(ifs, tmp); tmp = rtrim(tmp, "\r"); @@ -960,6 +1108,9 @@ int LaTeX::scanLogFile(TeXErrors & terr) getline(ifs, tmp); tmp = rtrim(tmp, "\r"); } + if (preamble_error) + // Add a note that the error is to be found in preamble + errstr += "\n" + to_utf8(_("(NOTE: The erroneous command is in the preamble)")); LYXERR(Debug::LATEX, "line: " << line << '\n' << "Desc: " << desc << '\n' << "Text: " << errstr); if (line == last_line) @@ -991,7 +1142,7 @@ int LaTeX::scanLogFile(TeXErrors & terr) retval |= TEX_WARNING; } else if (prefixIs(token, "Underfull ")) { retval |= TEX_WARNING; - } else if (contains(token, "Rerun to get citations")) { + } else if (!runparams.includeall && contains(token, "Rerun to get citations")) { // Natbib seems to use this. retval |= UNDEF_CIT; } else if (contains(token, "No pages of output") @@ -1485,4 +1636,42 @@ int LaTeX::scanBlgFile(DepTable & dep, TeXErrors & terr) } +int LaTeX::scanIlgFile(TeXErrors & terr) +{ + FileName const ilg_file(changeExtension(file.absFileName(), "ilg")); + LYXERR(Debug::LATEX, "Scanning ilg file: " << ilg_file); + + ifstream ifs(ilg_file.toFilesystemEncoding().c_str()); + string token; + int retval = NO_ERRORS; + + string prevtoken; + while (getline(ifs, token)) { + token = rtrim(token, "\r"); + smatch sub; + if (prefixIs(token, "!! ")) + prevtoken = token; + else if (!prevtoken.empty()) { + retval |= INDEX_ERROR; + string errstr = N_("Makeindex error: ") + prevtoken; + string msg = prevtoken + '\n'; + msg += token; + terr.insertError(0, + from_local8bit(errstr), + from_local8bit(msg)); + prevtoken.clear(); + } else if (prefixIs(token, "ERROR: ")) { + retval |= BIBTEX_ERROR; + string errstr = N_("Xindy error: ") + token.substr(6); + string msg = token; + terr.insertError(0, + from_local8bit(errstr), + from_local8bit(msg)); + } + } + return retval; +} + + + } // namespace lyx