-
Notifications
You must be signed in to change notification settings - Fork 2
ENH: adding manual compilation chain without latexmk #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hi @axtimhaus, thanks for raising this issue. When pytask-latex was created, I wanted a quick solution and was happy to find out that latexmk covers like 90% of all use-cases. In this case the package hits a wall. I like about your solution that it separates the complete build process in composable chunks or build steps which can be reused and rearranged depending on the user's needs. I have two comments:
Continuing the idea of the first bullet point and boiling down your API, I could imagine some API like this: @pytask.mark.latex(build_steps=["latex", "biber", "bib2gls", "latex"])
@pytask.mark.depends_on("doc.tex")
@pytask.mark.produces("doc.pdf")
def task_compile_doc():
pass Each string can be mapped to an internal build step or a user provides a custom one adhering to some specs. Some build steps might also need args or kwargs which we also need to figure out. But, maybe as a starting point, would you like to create a task without the
I think if we see the task, we will have better picture of the requirements for the API and you can decide whether it is worth it to work on the implementation. I do not have time to work on the implementation soon, but I could provide some sparring if you would like to take a stab at it. |
The problem with the DAG I encountered so far. My current workaround looks like that: ROOT_DIR = Path(__file__).parent.resolve()
ROOT_NAME = "documentation"
ROOT_TEX = ROOT_DIR / f"{ROOT_NAME}.tex"
ROOT_PDF = ROOT_DIR / f"{ROOT_NAME}.pdf"
_root_deps = latex_dependency_scanner.scanner.scan(ROOT_TEX)
ROOT_DEPS = [d for d in _root_deps if d.suffix != ".bib"]
ROOT_BIBS = [d for d in _root_deps if d.suffix == ".bib"]
def collect_symbols_files():
regex = re.compile(r"\\glsxtrresourcefile{([^{}]*)}", re.M)
tex_text = ROOT_TEX.read_text()
matches = regex.findall(tex_text)
return [ROOT_DIR / f"{m}.bib" for m in matches], [ROOT_DIR / f"{m}.glstex" for m in matches]
SYMBOLS_BIBS, SYMBOLS_GLSTEXS = collect_symbols_files()
@pytask.mark.depends_on(ROOT_DEPS)
@pytask.mark.depends_on(ROOT_TEX)
@pytask.mark.produces(f"{ROOT_NAME}.bcf")
@pytask.mark.produces(f"{ROOT_NAME}.aux")
def task_compile_latex_first():
subprocess.run(("pdflatex", *_LATEX_OPTS, ROOT_TEX))
@pytask.mark.depends_on(ROOT_TEX)
@pytask.mark.depends_on(f"{ROOT_NAME}.bcf")
@pytask.mark.produces(f"{ROOT_NAME}.bbl")
def task_run_biber():
subprocess.run(("biber", ROOT_NAME))
@pytask.mark.depends_on(ROOT_TEX)
@pytask.mark.depends_on(f"{ROOT_NAME}.aux")
@pytask.mark.depends_on(SYMBOLS_BIBS)
@pytask.mark.produces(SYMBOLS_GLSTEXS)
def task_run_bib2gls():
subprocess.run(("bib2gls", ROOT_NAME))
@pytask.mark.depends_on(ROOT_DEPS)
@pytask.mark.depends_on(SYMBOLS_GLSTEXS)
@pytask.mark.depends_on(f"{ROOT_NAME}.bbl")
@pytask.mark.depends_on(ROOT_TEX)
@pytask.mark.produces(ROOT_PDF)
def task_compile_latex_final():
subprocess.run(("pdflatex", *_LATEX_OPTS, ROOT_TEX)) I see, that this approach is kind of a lye, because the Latex runs produce much more than is denoted in the tasks. In general I have no problem with coding this stuff myself. |
Thanks for providing your workaround! I am less worried about that some products are left out - latex just works with inplace changes - and more that it is hard for users to specify dependencies and products correctly, for example the PDF, and then reporting issues. I completely agree with you that the build steps should be composable. This is what I had in mind and I think it has the flexibility you want. We assume that we have predefined internal build steps for latex and biber. First, you would set up a function which compiles your sources with bib2gls. def compile_bib2gls(path_to_tex, path_to_pdf, **kwargs):
return subprocess.run(("bib2gls", "--dir", path_to_pdf.parent.as_posix(), path_to_pdf.with_suffix("").name), check=True) The arguments of the function follow the specification of the API for build steps. A build step can receive arguments by specifying their name and collect unnecessary but provided arguments with Then, your build step is integrated alongside the predefined internal ones. @pytask.mark.latex(build_steps=["latex", "biber", compile_bib2gls, "latex"])
@pytask.mark.depends_on("doc.tex")
@pytask.mark.produces("doc.pdf")
def task_compile_doc():
pass What do you think about this approach? Do you see any limitations and do you think the API can be easily understood? A little bit of thinking needs to go into the signature of the Internally, there is a function for compiling the document which loops over all build steps, provides arguments to the functions and executes them. This is super preliminary. def compile_latex_document(build_steps, ...):
for step in build_steps:
if isintance(step, str)
func = _PREDEFINED_BUILD_STEPS[step]
else:
func = step
status = func(**kwargs)
check_status(status) |
Sounds good. Regarding the import pytask.latex.build_steps as bs
@pytask.mark.latex(build_steps=[
bs.latex(("", ...)),
bs.biber(("", ...))
bs.latex(("", ...))
bs.latex(("", ...))
])
@pytask.mark.depends_on("doc.tex")
@pytask.mark.produces("doc.pdf")
def task_compile_doc():
pass So the internal compile function just needs to be: def compile_latex_document(build_steps, ...):
for step in build_steps:
status = build_step(**kwargs)
check_status(status) Which |
I like the constructor functions for the build steps! They are as short as strings and have args and kwargs and dont require something like For backward compatibility we should provide the fallback to latexmk if it exists, then latex, biber, latex, latex. Nice, feels like we have an idea. Should we start with a plan for the implementation or do you see other obstacles? |
Just a proposal. We could split the implementation into two PRs.
|
We have I plan I think. |
Hi @axtimhaus, I am going to close this issue now because the main goal, customizable build steps, has been achieved. Your contribution would be highly welcomed if you would like to add more compile chains or compile steps. Thanks a lot for all your effort and time! 🙇♂️❤️ |
Is your feature request related to a problem?
As I mentioned in pytask-dev/latex-dependency-scanner#12 I have problems with the compile chain if I want to use
bib2gls
additionally.latexmk
does not supportbib2gls
and so I have to do it manually.bib2gls
needs the main .aux file to run, so latex must be compiled one time, thanbib2gls
must be run and afterwards the two final latex compilations.Describe the solution you'd like
The current system is really rigid, just one entry point for latex. I would suggest a more flexible way in addition to that to create distinct tasks to latex compilation,
biber
running andbib2gls
running as well as other indexing engines. So one could define different deps for the first and the final latex runs.I would like to discuss how this API could look like.
My first suggestion is something like:
The text was updated successfully, but these errors were encountered: